Suggest.java revision ed9986824e1339855376771ad29fae4de921a029
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
414ed9986824e1339855376771ad29fae4de921a029Jean Chalard        final SuggestedWords.Builder builder;
4158553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard        if (DBG) {
416719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard            final CharSequence autoCorrectionSuggestion = mSuggestions.get(0);
417719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard            final int autoCorrectionSuggestionScore = mScores[0];
418719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard            double normalizedScore = BinaryDictionary.calcNormalizedScore(
4198abd15b59f87f2738b0d27b7c24a126b1450a17cJean Chalard                    typedWord, autoCorrectionSuggestion.toString(),
420719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard                    autoCorrectionSuggestionScore);
421e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            ArrayList<SuggestedWords.SuggestedWordInfo> scoreInfoList =
4228553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                    new ArrayList<SuggestedWords.SuggestedWordInfo>();
423e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
424e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            for (int i = 0; i < mScores.length; ++i) {
4258553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                if (normalizedScore > 0) {
426071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka                    final String scoreThreshold = String.format("%d (%4.2f)", mScores[i],
427071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka                            normalizedScore);
428e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    scoreInfoList.add(
429e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            new SuggestedWords.SuggestedWordInfo(scoreThreshold, false));
4308553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                    normalizedScore = 0.0;
4318553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                } else {
432e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    final String score = Integer.toString(mScores[i]);
433e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    scoreInfoList.add(new SuggestedWords.SuggestedWordInfo(score, false));
4348553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                }
4358553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard            }
436e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            for (int i = mScores.length; i < mSuggestions.size(); ++i) {
437e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
4388553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard            }
439ed9986824e1339855376771ad29fae4de921a029Jean Chalard            builder = new SuggestedWords.Builder().addWords(mSuggestions, scoreInfoList)
440ed9986824e1339855376771ad29fae4de921a029Jean Chalard                    .setAllowsToBeAutoCorrected(allowsToBeAutoCorrected)
441ed9986824e1339855376771ad29fae4de921a029Jean Chalard                    .setHasAutoCorrection(hasAutoCorrection);
442ed9986824e1339855376771ad29fae4de921a029Jean Chalard        } else {
443ed9986824e1339855376771ad29fae4de921a029Jean Chalard            builder = new SuggestedWords.Builder().addWords(mSuggestions, null)
44467af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard                    .setAllowsToBeAutoCorrected(allowsToBeAutoCorrected)
44567af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard                    .setHasAutoCorrection(hasAutoCorrection);
4468553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard        }
447ed9986824e1339855376771ad29fae4de921a029Jean Chalard
448ed9986824e1339855376771ad29fae4de921a029Jean Chalard        boolean autoCorrectionAvailable = hasAutoCorrection;
449ed9986824e1339855376771ad29fae4de921a029Jean Chalard        if (correctionMode == Suggest.CORRECTION_FULL
450ed9986824e1339855376771ad29fae4de921a029Jean Chalard                || correctionMode == Suggest.CORRECTION_FULL_BIGRAM) {
451ed9986824e1339855376771ad29fae4de921a029Jean Chalard            autoCorrectionAvailable |= !allowsToBeAutoCorrected;
452ed9986824e1339855376771ad29fae4de921a029Jean Chalard        }
453ed9986824e1339855376771ad29fae4de921a029Jean Chalard        // Don't auto-correct words with multiple capital letter
454ed9986824e1339855376771ad29fae4de921a029Jean Chalard        autoCorrectionAvailable &= !wordComposer.isMostlyCaps();
455ed9986824e1339855376771ad29fae4de921a029Jean Chalard        builder.setTypedWordValid(!allowsToBeAutoCorrected).setHasMinimalSuggestion(
456ed9986824e1339855376771ad29fae4de921a029Jean Chalard                autoCorrectionAvailable);
457ed9986824e1339855376771ad29fae4de921a029Jean Chalard        if (Suggest.shouldBlockAutoCorrectionBySafetyNet(builder, this, mAutoCorrectionThreshold)) {
458ed9986824e1339855376771ad29fae4de921a029Jean Chalard            builder.setShouldBlockAutoCorrectionBySafetyNet();
459ed9986824e1339855376771ad29fae4de921a029Jean Chalard        }
460ed9986824e1339855376771ad29fae4de921a029Jean Chalard        return builder;
461923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
462923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
4635a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
464e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    public boolean addWord(final char[] word, final int offset, final int length, int score,
4656e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard            final int dicTypeId, final int dataType) {
4666e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard        int dataTypeForLog = dataType;
467e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final ArrayList<CharSequence> suggestions;
468e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final int[] sortedScores;
469e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final int prefMaxSuggestions;
4706e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard        if (dataType == Dictionary.BIGRAM) {
471979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            suggestions = mBigramSuggestions;
472e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            sortedScores = mBigramScores;
473979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            prefMaxSuggestions = PREF_MAX_BIGRAMS;
474979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
475979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            suggestions = mSuggestions;
476e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            sortedScores = mScores;
477979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            prefMaxSuggestions = mPrefMaxSuggestions;
478979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
479979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
480923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int pos = 0;
481979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
482923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if it's the same word, only caps are different
483cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) {
484d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            // TODO: remove this surrounding if clause and move this logic to
485d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            // getSuggestedWordBuilder.
486d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            if (suggestions.size() > 0) {
487e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                final String currentHighestWord = suggestions.get(0).toString();
488d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // If the current highest word is also equal to typed word, we need to compare
489d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // frequency to determine the insertion position. This does not ensure strictly
490d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // correct ordering, but ensures the top score is on top which is enough for
491d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // removing duplicates correctly.
492cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                if (StringUtils.equalsIgnoreCase(currentHighestWord, word, offset, length)
493e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                        && score <= sortedScores[0]) {
494d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                    pos = 1;
495d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                }
496d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            }
497923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
4986e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard            if (dataType == Dictionary.UNIGRAM) {
499979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                // Check if the word was already added before (by bigram data)
500979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                int bigramSuggestion = searchBigramSuggestion(word,offset,length);
501979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if(bigramSuggestion >= 0) {
5026e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard                    dataTypeForLog = Dictionary.BIGRAM;
503979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    // turn freq from bigram into multiplier specified above
504e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    double multiplier = (((double) mBigramScores[bigramSuggestion])
505979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            / MAXIMUM_BIGRAM_FREQUENCY)
506979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
507979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + BIGRAM_MULTIPLIER_MIN;
508979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    /* Log.d(TAG,"bigram num: " + bigramSuggestion
509979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + "  wordB: " + mBigramSuggestions.get(bigramSuggestion).toString()
510e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            + "  currentScore: " + score + "  bigramScore: "
511e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            + mBigramScores[bigramSuggestion]
512979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + "  multiplier: " + multiplier); */
513e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    score = (int)Math.round((score * multiplier));
514979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
515979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
516979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
517e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            // Check the last one's score and bail
518e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            if (sortedScores[prefMaxSuggestions - 1] >= score) return true;
519923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            while (pos < prefMaxSuggestions) {
520e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                if (sortedScores[pos] < score
521e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                        || (sortedScores[pos] == score && length < suggestions.get(pos).length())) {
522923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    break;
523923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                }
524923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                pos++;
525923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
526923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
527923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (pos >= prefMaxSuggestions) {
528923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
529923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
530979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
531e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
532e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        sortedScores[pos] = score;
5334e01afc520da212b73804164d4d5a1c62239b02aJean Chalard        final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength());
53435f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka        // TODO: Must pay attention to locale when changing case.
5350b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        if (mIsAllUpperCase) {
5360b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa            sb.append(new String(word, offset, length).toUpperCase());
5370b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        } else if (mIsFirstCharCapitalized) {
538359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            sb.append(Character.toUpperCase(word[offset]));
539359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            if (length > 1) {
540359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani                sb.append(word, offset + 1, length - 1);
541359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            }
542359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani        } else {
543359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            sb.append(word, offset, length);
544359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani        }
545117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
546c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard            sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
547c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard        }
548979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        suggestions.add(pos, sb);
549979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (suggestions.size() > prefMaxSuggestions) {
5504e01afc520da212b73804164d4d5a1c62239b02aJean Chalard            final CharSequence garbage = suggestions.remove(prefMaxSuggestions);
551923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (garbage instanceof StringBuilder) {
5524e01afc520da212b73804164d4d5a1c62239b02aJean Chalard                StringBuilderPool.recycle((StringBuilder)garbage);
553923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
554979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
555979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
556923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
557923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
558923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
559923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
560979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private int searchBigramSuggestion(final char[] word, final int offset, final int length) {
561979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // TODO This is almost O(n^2). Might need fix.
562979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // search whether the word appeared in bigram data
563979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        int bigramSuggestSize = mBigramSuggestions.size();
564979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        for(int i = 0; i < bigramSuggestSize; i++) {
565979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if(mBigramSuggestions.get(i).length() == length) {
566979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                boolean chk = true;
567979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                for(int j = 0; j < length; j++) {
568979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    if(mBigramSuggestions.get(i).charAt(j) != word[offset+j]) {
569979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                        chk = false;
570979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                        break;
571979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    }
572979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
573979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if(chk) return i;
574979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
575979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
576979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
577979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return -1;
578979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
579979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
5808fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static void collectGarbage(ArrayList<CharSequence> suggestions,
5818fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka            int prefMaxSuggestions) {
5824e01afc520da212b73804164d4d5a1c62239b02aJean Chalard        int poolSize = StringBuilderPool.getSize();
583979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        int garbageSize = suggestions.size();
584979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        while (poolSize < prefMaxSuggestions && garbageSize > 0) {
5854e01afc520da212b73804164d4d5a1c62239b02aJean Chalard            final CharSequence garbage = suggestions.get(garbageSize - 1);
5864e01afc520da212b73804164d4d5a1c62239b02aJean Chalard            if (garbage instanceof StringBuilder) {
5874e01afc520da212b73804164d4d5a1c62239b02aJean Chalard                StringBuilderPool.recycle((StringBuilder)garbage);
588923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                poolSize++;
589923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
590923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            garbageSize--;
591923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
592979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (poolSize == prefMaxSuggestions + 1) {
593923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            Log.w("Suggest", "String pool got too big: " + poolSize);
594923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
595979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        suggestions.clear();
596923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
59736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
59836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    public void close() {
599c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        final Set<Dictionary> dictionaries = new HashSet<Dictionary>();
600c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        dictionaries.addAll(mUnigramDictionaries.values());
601c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        dictionaries.addAll(mBigramDictionaries.values());
602c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        for (final Dictionary dictionary : dictionaries) {
603c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            dictionary.close();
60436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani        }
605c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        mMainDict = null;
60636fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
607cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka
608cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka    // TODO: Resolve the inconsistencies between the native auto correction algorithms and
609cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka    // this safety net
610cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka    public static boolean shouldBlockAutoCorrectionBySafetyNet(
61113fb8fb775f77610105115d7cb30f6177ec6d771Jean Chalard            final SuggestedWords.Builder suggestedWordsBuilder, final Suggest suggest,
61213fb8fb775f77610105115d7cb30f6177ec6d771Jean Chalard            final double autoCorrectionThreshold) {
613cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        // Safety net for auto correction.
614cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        // Actually if we hit this safety net, it's actually a bug.
615cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        if (suggestedWordsBuilder.size() <= 1 || suggestedWordsBuilder.isTypedWordValid()) {
616cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            return false;
617cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        }
618cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        // If user selected aggressive auto correction mode, there is no need to use the safety
619cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        // net.
62013fb8fb775f77610105115d7cb30f6177ec6d771Jean Chalard        if (0 == autoCorrectionThreshold) {
621cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            return false;
622cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        }
623cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        final CharSequence typedWord = suggestedWordsBuilder.getWord(0);
624cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
625cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        // we should not use net because relatively edit distance can be big.
626cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        if (typedWord.length() < Suggest.MINIMUM_SAFETY_NET_CHAR_LENGTH) {
627cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            return false;
628cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        }
629cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        final CharSequence suggestionWord = suggestedWordsBuilder.getWord(1);
630cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        final int typedWordLength = typedWord.length();
631cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        final int maxEditDistanceOfNativeDictionary =
632cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
633cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        final int distance = BinaryDictionary.editDistance(
634cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                typedWord.toString(), suggestionWord.toString());
635cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        if (DBG) {
636cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            Log.d(TAG, "Autocorrected edit distance = " + distance
637cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                    + ", " + maxEditDistanceOfNativeDictionary);
638cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        }
639cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        if (distance > maxEditDistanceOfNativeDictionary) {
640cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            if (DBG) {
641cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestionWord);
642cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
643cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                        + "Turning off auto-correction.");
644cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            }
645cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            return true;
646cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        } else {
647cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            return false;
648cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        }
649cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka    }
650923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
651