Suggest.java revision a28a05e971cc242b338331a3b78276fa95188d19
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 Project 22c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalardimport com.android.inputmethod.keyboard.Keyboard; 23043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalardimport com.android.inputmethod.keyboard.ProximityInfo; 24def4551c2a570e7f575b2e9303506d790c2f335fJean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 25043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard 2633e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaokaimport java.io.File; 27fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.ArrayList; 289da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalardimport java.util.Comparator; 29c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.HashSet; 30cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalardimport java.util.Locale; 311b06b59e28743b713947947437ea5b312477f808Jean Chalardimport java.util.concurrent.ConcurrentHashMap; 32fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka 33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 34e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * This class loads a dictionary and provides a list of suggestions for a given sequence of 35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * characters. This includes corrections and completions. 36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 37a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class Suggest { 3882411d47ba7e8133ed2390c6920945e139a738cesatok public static final String TAG = Suggest.class.getSimpleName(); 39cdbbea735f590784791f0c1fe33a514c4e864836satok 40f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka // Session id for 41f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka // {@link #getSuggestedWords(WordComposer,CharSequence,ProximityInfo,boolean,int)}. 42f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka public static final int SESSION_TYPING = 0; 43f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka public static final int SESSION_GESTURE = 1; 44f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka 455475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_OFF 46923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public static final int CORRECTION_NONE = 0; 475475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_ON 484606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard public static final int CORRECTION_FULL = 1; 49979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 50369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public interface SuggestInitializationListener { 51369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable); 52369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 53369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka 548553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard private static final boolean DBG = LatinImeLogger.sDBG; 5582411d47ba7e8133ed2390c6920945e139a738cesatok 566080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge private Dictionary mMainDictionary; 5767fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard private ContactsBinaryDictionary mContactsDict; 586234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard private final ConcurrentHashMap<String, Dictionary> mDictionaries = 595f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newConcurrentHashMap(); 60979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 610998c48ac30294b7b6b70257b390962e930b59e1Jean Chalard public static final int MAX_SUGGESTIONS = 18; 6234386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani 630028ed3627ff4f37a62a80f3b2c857e373cd5090satok private float mAutoCorrectionThreshold; 64979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 658e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard // Locale used for upper- and title-casing words 66369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka private final Locale mLocale; 678e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard 68369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public Suggest(final Context context, final Locale locale, 69369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka final SuggestInitializationListener listener) { 7079eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka initAsynchronously(context, locale, listener); 718e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard mLocale = locale; 72979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 73979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 7424aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard /* package for test */ Suggest(final Context context, final File dictionary, 7524aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard final long startOffset, final long length, final Locale locale) { 76f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary, 77f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard startOffset, length /* useFullEditDistance */, false, locale); 788e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard mLocale = locale; 796080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = mainDict; 80d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict); 8133e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka } 8233e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka 8379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka private void initAsynchronously(final Context context, final Locale locale, 8479eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka final SuggestInitializationListener listener) { 8579eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka resetMainDict(context, locale, listener); 863af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 873af9f05f2916e376f265974c820c369a6c63a780Jean Chalard 881b06b59e28743b713947947437ea5b312477f808Jean Chalard private static void addOrReplaceDictionary( 891b06b59e28743b713947947437ea5b312477f808Jean Chalard final ConcurrentHashMap<String, Dictionary> dictionaries, 901b06b59e28743b713947947437ea5b312477f808Jean Chalard final String key, final Dictionary dict) { 913439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka final Dictionary oldDict = (dict == null) 923439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka ? dictionaries.remove(key) 933439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka : dictionaries.put(key, dict); 943439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka if (oldDict != null && dict != oldDict) { 953439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka oldDict.close(); 963439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 973439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 983439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka 9979eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka public void resetMainDict(final Context context, final Locale locale, 10079eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka final SuggestInitializationListener listener) { 1016080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = null; 10279eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka if (listener != null) { 10379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); 104369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 1053af9f05f2916e376f265974c820c369a6c63a780Jean Chalard new Thread("InitializeBinaryDictionary") { 106904baab25a4c6ec5d9c4bf7e562154e3f544d296satok @Override 1073af9f05f2916e376f265974c820c369a6c63a780Jean Chalard public void run() { 108f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard final DictionaryCollection newMainDict = 109f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard DictionaryFactory.createMainDictionaryFromManager(context, locale); 110d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict); 1116080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = newMainDict; 11279eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka if (listener != null) { 11379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); 114369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 1153af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 1163af9f05f2916e376f265974c820c369a6c63a780Jean Chalard }.start(); 117cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard } 118cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard 119c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // The main dictionary could have been loaded asynchronously. Don't cache the return value 120c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // of this method. 121e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani public boolean hasMainDictionary() { 1226080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge return null != mMainDictionary && mMainDictionary.isInitialized(); 1236080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge } 1246080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge 1256080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge public Dictionary getMainDictionary() { 1266080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge return mMainDictionary; 127e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani } 128e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani 12967fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard public ContactsBinaryDictionary getContactsDictionary() { 13014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard return mContactsDict; 13114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard } 13214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard 1331b06b59e28743b713947947437ea5b312477f808Jean Chalard public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() { 1346234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard return mDictionaries; 135bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 136bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 137923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 138923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted 139f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard * before the main dictionary, if set. This refers to the system-managed user dictionary. 140923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 14167fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard public void setUserDictionary(UserBinaryDictionary userDictionary) { 142d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary); 143923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 1442bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 1452bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer /** 146699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove 147699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * the contacts dictionary by passing null to this method. In this case no contacts dictionary 148699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * won't be used. 1492bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer */ 15067fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) { 15114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard mContactsDict = contactsDictionary; 152d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary); 1532bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 154e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa 15567fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) { 156d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary); 157979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 158979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 1590028ed3627ff4f37a62a80f3b2c857e373cd5090satok public void setAutoCorrectionThreshold(float threshold) { 1601b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka mAutoCorrectionThreshold = threshold; 161b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda } 162b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda 16328eeb35d149468514a65379e9d0d1672cf26981eJean Chalard public SuggestedWords getSuggestedWords( 16428eeb35d149468514a65379e9d0d1672cf26981eJean Chalard final WordComposer wordComposer, CharSequence prevWordForBigram, 1653979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) { 166979f8690967ff5409fe18f5085858ccdb8e0ccf1satok LatinImeLogger.onStartSuggestion(prevWordForBigram); 167d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka if (wordComposer.isBatchMode()) { 1683979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka return getSuggestedWordsForBatchInput( 1693979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka wordComposer, prevWordForBigram, proximityInfo, sessionId); 170d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } else { 171d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, 172d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka isCorrectionEnabled); 173d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 174d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 175d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 176d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka // Retrieves suggestions for the typing input. 177d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka private SuggestedWords getSuggestedWordsForTypingInput( 178d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final WordComposer wordComposer, CharSequence prevWordForBigram, 179d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) { 18010abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); 1812d2e3480338b97b55f1a22bf2bfe89c52ba866e2Jean Chalard final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, 1822d2e3480338b97b55f1a22bf2bfe89c52ba866e2Jean Chalard MAX_SUGGESTIONS); 1831b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani 184c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard final String typedWord = wordComposer.getTypedWord(); 18510abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard final String consideredWord = trailingSingleQuotesCount > 0 18610abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) 187117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard : typedWord; 188d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED); 189979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 190c677b0071d51a277413079b30f2215605637aa6bJean Chalard final WordComposer wordComposerForLookup; 191c677b0071d51a277413079b30f2215605637aa6bJean Chalard if (trailingSingleQuotesCount > 0) { 192c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup = new WordComposer(wordComposer); 193c677b0071d51a277413079b30f2215605637aa6bJean Chalard for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { 194c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup.deleteLast(); 195c677b0071d51a277413079b30f2215605637aa6bJean Chalard } 196c677b0071d51a277413079b30f2215605637aa6bJean Chalard } else { 197c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup = wordComposer; 198c677b0071d51a277413079b30f2215605637aa6bJean Chalard } 199d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard 200d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard for (final String key : mDictionaries.keySet()) { 201d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard final Dictionary dictionary = mDictionaries.get(key); 202d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard suggestionsSet.addAll(dictionary.getSuggestions( 203d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard wordComposerForLookup, prevWordForBigram, proximityInfo)); 204923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 2059f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 2067b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard final CharSequence whitelistedWord; 2077b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard if (suggestionsSet.isEmpty()) { 2087b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = null; 2097b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) { 2107b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = null; 2117b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } else { 2127b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = suggestionsSet.first().mWord; 2137b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } 2147b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard 2158c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard // The word can be auto-corrected if it has a whitelist entry that is not itself, 2168c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard // or if it's a 2+ characters non-word (i.e. it's not in the dictionary). 217caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard final boolean allowsToBeAutoCorrected = (null != whitelistedWord 218caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard && !whitelistedWord.equals(consideredWord)) 2198c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard || (consideredWord.length() > 1 && !AutoCorrection.isInTheDictionary(mDictionaries, 2208c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard consideredWord, wordComposer.isFirstCharCapitalized())); 221caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard 22267af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard final boolean hasAutoCorrection; 223966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because 224966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // any attempt to do auto-correction is already shielded with a test for this flag; at the 225966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // same time, it feels wrong that the SuggestedWord object includes information about 226966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the current settings. It may also be useful to know, when the setting is off, whether 227966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the word *would* have been auto-corrected. 2282549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord() 229e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard || suggestionsSet.isEmpty() || wordComposer.hasDigits() 2302549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard || wordComposer.isMostlyCaps() || wordComposer.isResumed() 2312549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard || !hasMainDictionary()) { 23290d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // If we don't have a main dictionary, we never want to auto-correct. The reason for 23390d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // this is, the user may have a contact whose name happens to match a valid word in 23490d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // their language, and it will unexpectedly auto-correct. For example, if the user 23590d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // types in English with no dictionary and has a "Will" in their contact list, "will" 23690d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no 23790d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // auto-correct. 238ea578f6b1dbcf04ffcc9c673f72a38ed2cfecdfcJean Chalard hasAutoCorrection = false; 23994b20c90d86aa042c2f361597665045271956decJean Chalard } else { 2401343d27de30c4010c54576d6c8bbb052c7630cbeJean Chalard hasAutoCorrection = AutoCorrection.suggestionExceedsAutoCorrectionThreshold( 2411343d27de30c4010c54576d6c8bbb052c7630cbeJean Chalard suggestionsSet.first(), consideredWord, mAutoCorrectionThreshold); 24294b20c90d86aa042c2f361597665045271956decJean Chalard } 2439f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 244ed83d4b14366b9799bf94c3f3486dc14ebd15d0fJean Chalard final ArrayList<SuggestedWordInfo> suggestionsContainer = 2455f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSet); 2465110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final int suggestionsCount = suggestionsContainer.size(); 247f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); 248f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard final boolean isAllUpperCase = wordComposer.isAllUpperCase(); 2495110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) { 2505110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard for (int i = 0; i < suggestionsCount; ++i) { 2515110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 2525110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 2535110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized, 2545110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard trailingSingleQuotesCount); 2555110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard suggestionsContainer.set(i, transformedWordInfo); 2565110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard } 2575110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard } 2585110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard 2595110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard for (int i = 0; i < suggestionsCount; ++i) { 260ed83d4b14366b9799bf94c3f3486dc14ebd15d0fJean Chalard final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 2615110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict); 262bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 263bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 264f89a75134b03bd2675c85249a184c09f83c6f80cJean Chalard if (!TextUtils.isEmpty(typedWord)) { 265bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, 26624eec0fa680f97e64d1fa0df754acbad95ed9a76Jean Chalard SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, 26724eec0fa680f97e64d1fa0df754acbad95ed9a76Jean Chalard Dictionary.TYPE_USER_TYPED)); 26828eeb35d149468514a65379e9d0d1672cf26981eJean Chalard } 269bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard SuggestedWordInfo.removeDups(suggestionsContainer); 2709f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 271def4551c2a570e7f575b2e9303506d790c2f335fJean Chalard final ArrayList<SuggestedWordInfo> suggestionsList; 272bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard if (DBG && !suggestionsContainer.isEmpty()) { 273bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer); 274ed9986824e1339855376771ad29fae4de921a029Jean Chalard } else { 275bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = suggestionsContainer; 2768553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 277ed9986824e1339855376771ad29fae4de921a029Jean Chalard 2784a08b2f0e4d0ee7f1d89b4eb3c77c37d987584eaJean Chalard return new SuggestedWords(suggestionsList, 279b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // TODO: this first argument is lying. If this is a whitelisted word which is an 280b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // actual word, it says typedWordValid = false, which looks wrong. We should either 281b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // rename the attribute or change the value. 28202f1c1534c2060aaea7a9a020ce87f6c5ff5d8e0Jean Chalard !allowsToBeAutoCorrected /* typedWordValid */, 2832549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard hasAutoCorrection, /* willAutoCorrect */ 28403a35170751a635332c00bf6c272a0127a255cf6Jean Chalard false /* isPunctuationSuggestions */, 2850142b997bf18f5d07e83b3fd403f0b3ea4736040satok false /* isObsoleteSuggestions */, 286f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard !wordComposer.isComposingWord() /* isPrediction */); 287923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 288923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 289d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka // Retrieves suggestions for the batch input. 290d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka private SuggestedWords getSuggestedWordsForBatchInput( 291d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final WordComposer wordComposer, CharSequence prevWordForBigram, 2923979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka final ProximityInfo proximityInfo, int sessionId) { 293d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, 294d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka MAX_SUGGESTIONS); 295d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 296d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka // At second character typed, search the unigrams (scores being affected by bigrams) 297d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka for (final String key : mDictionaries.keySet()) { 29846fc768e54e3d52003645494552f9e686f28f20fJean Chalard // Skip User history dictionary for lookup 29946fc768e54e3d52003645494552f9e686f28f20fJean Chalard // TODO: The user history dictionary should just override getSuggestionsWithSessionId 30046fc768e54e3d52003645494552f9e686f28f20fJean Chalard // to make sure it doesn't return anything and we should remove this test 30146fc768e54e3d52003645494552f9e686f28f20fJean Chalard if (key.equals(Dictionary.TYPE_USER_HISTORY)) { 302d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka continue; 303d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 304d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final Dictionary dictionary = mDictionaries.get(key); 3053979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId( 3063979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka wordComposer, prevWordForBigram, proximityInfo, sessionId)); 307d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 308d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 30987cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka for (SuggestedWordInfo wordInfo : suggestionsSet) { 31087cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict); 31187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka } 31287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka 313d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final ArrayList<SuggestedWordInfo> suggestionsContainer = 3145f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSet); 315eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int suggestionsCount = suggestionsContainer.size(); 3161eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); 3171eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isAllUpperCase = wordComposer.isAllUpperCase(); 318eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang if (isFirstCharCapitalized || isAllUpperCase) { 319eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang for (int i = 0; i < suggestionsCount; ++i) { 320eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 321eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 322eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized, 323eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang 0 /* trailingSingleQuotesCount */); 324eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang suggestionsContainer.set(i, transformedWordInfo); 325eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 326eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 327d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 328d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka SuggestedWordInfo.removeDups(suggestionsContainer); 329eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // In the batch input mode, the most relevant suggested word should act as a "typed word" 330eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). 331d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka return new SuggestedWords(suggestionsContainer, 332d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka true /* typedWordValid */, 333eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang false /* willAutoCorrect */, 334d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isPunctuationSuggestions */, 335d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isObsoleteSuggestions */, 336d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isPrediction */); 337d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 338d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 3390d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( 3407e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) { 3417e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo typedWordInfo = suggestions.get(0); 3427e518d8b8358c96b94b900f0917cdc5fd8190ce1satok typedWordInfo.setDebugString("+"); 3430d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final int suggestionsSize = suggestions.size(); 3440d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final ArrayList<SuggestedWordInfo> suggestionsList = 3455f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSize); 3467e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(typedWordInfo); 3470d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // Note: i here is the index in mScores[], but the index in mSuggestions is one more 3480d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // than i because we added the typed word to mSuggestions without touching mScores. 3497e518d8b8358c96b94b900f0917cdc5fd8190ce1satok for (int i = 0; i < suggestionsSize - 1; ++i) { 3507e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo cur = suggestions.get(i + 1); 3510028ed3627ff4f37a62a80f3b2c857e373cd5090satok final float normalizedScore = BinaryDictionary.calcNormalizedScore( 352db1939dbaa1de59eaf5693e2c89b02b323e9aac8satok typedWord, cur.toString(), cur.mScore); 3530d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final String scoreInfoString; 3540d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard if (normalizedScore > 0) { 3557e518d8b8358c96b94b900f0917cdc5fd8190ce1satok scoreInfoString = String.format("%d (%4.2f)", cur.mScore, normalizedScore); 3560d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } else { 3577e518d8b8358c96b94b900f0917cdc5fd8190ce1satok scoreInfoString = Integer.toString(cur.mScore); 3580d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 3597e518d8b8358c96b94b900f0917cdc5fd8190ce1satok cur.setDebugString(scoreInfoString); 3607e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(cur); 3610d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 3620d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard return suggestionsList; 3630d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 3640d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard 365a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka private static final class SuggestedWordInfoComparator 366a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka implements Comparator<SuggestedWordInfo> { 3679da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard // This comparator ranks the word info with the higher frequency first. That's because 3689da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard // that's the order we want our elements in. 3699da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard @Override 3709da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) { 3719da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mScore > o2.mScore) return -1; 3729da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mScore < o2.mScore) return 1; 3739da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mCodePointCount < o2.mCodePointCount) return -1; 3749da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mCodePointCount > o2.mCodePointCount) return 1; 3759da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard return o1.mWord.toString().compareTo(o2.mWord.toString()); 3769da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard } 3779da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard } 3789da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator = 3799da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard new SuggestedWordInfoComparator(); 3809da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard 381ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard private static SuggestedWordInfo getTransformedSuggestedWordInfo( 382ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, 383ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) { 3849011b89f4ea0d73f1ad78b2dd0a6557b950fddd9Jean Chalard final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); 385ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard if (isAllUpperCase) { 386ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard sb.append(wordInfo.mWord.toString().toUpperCase(locale)); 387ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } else if (isFirstCharCapitalized) { 388ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale)); 389ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } else { 390ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard sb.append(wordInfo.mWord); 391ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 392ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { 393ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); 394ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 39524eec0fa680f97e64d1fa0df754acbad95ed9a76Jean Chalard return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict); 396ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 397ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard 39836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani public void close() { 3995f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet(); 4006234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard dictionaries.addAll(mDictionaries.values()); 401c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka for (final Dictionary dictionary : dictionaries) { 402c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka dictionary.close(); 40336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 4046080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = null; 40536fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 406923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 407