Suggest.java revision f4223452119f9ff8b52f026f7ef92d961736dc51
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.AutoText;
21923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils;
22923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Log;
23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View;
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2533e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaokaimport java.io.File;
26fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.ArrayList;
27fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.Arrays;
28c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.HashMap;
29c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.HashSet;
30cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalardimport java.util.Locale;
31c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.Map;
32c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.Set;
33fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka
34923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
35e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * This class loads a dictionary and provides a list of suggestions for a given sequence of
36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * characters. This includes corrections and completions.
37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpublic class Suggest implements Dictionary.WordCallback {
39923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
4082411d47ba7e8133ed2390c6920945e139a738cesatok    public static final String TAG = Suggest.class.getSimpleName();
41cdbbea735f590784791f0c1fe33a514c4e864836satok
42979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int APPROX_MAX_WORD_LENGTH = 32;
43979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public static final int CORRECTION_NONE = 0;
45923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public static final int CORRECTION_BASIC = 1;
46923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public static final int CORRECTION_FULL = 2;
47979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int CORRECTION_FULL_BIGRAM = 3;
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;
70979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
71f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard    // TODO: this value seems unused. Remove it?
72f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard    public static final int DIC_TYPE_LAST_ID = 5;
7334386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
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
869f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok    private AutoCorrection mAutoCorrection;
879f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
884250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard    private Dictionary mMainDict;
89bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    private WhitelistDictionary mWhiteListDictionary;
90c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka    private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>();
91c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka    private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>();
92979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
9386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka    private int mPrefMaxSuggestions = 18;
9434386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
95979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private static final int PREF_MAX_BIGRAMS = 60;
96979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
975ef096f5f601c759e8a4a888aaca91ac5ccd9974Tadashi G. Takaoka    private boolean mQuickFixesEnabled;
98fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani
991b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private double mAutoCorrectionThreshold;
100e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    private int[] mScores = new int[mPrefMaxSuggestions];
101e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    private int[] mBigramScores = new int[PREF_MAX_BIGRAMS];
102979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
103bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
104979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    ArrayList<CharSequence> mBigramSuggestions  = new ArrayList<CharSequence>();
105bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
106e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    private CharSequence mTypedWord;
1070b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
1080b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    // TODO: Remove these member variables by passing more context to addWord() callback method
1090b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
1100b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsAllUpperCase;
111923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
112923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mCorrectionMode = CORRECTION_BASIC;
113923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
114cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    public Suggest(Context context, int dictionaryResId, Locale locale) {
1154250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        init(context, DictionaryFactory.createDictionaryFromManager(context, locale,
116cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard                dictionaryResId));
117979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
118979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
119c899038eee5c01d520a2707cca01ee093a674d05Jean Chalard    /* package for test */ Suggest(Context context, File dictionary, long startOffset, long length,
120c899038eee5c01d520a2707cca01ee093a674d05Jean Chalard            Flag[] flagArray) {
1214250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        init(null, DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset,
1224250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard                length, flagArray));
1239f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok    }
1249f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
1254250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard    private void init(Context context, Dictionary mainDict) {
1263439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        mMainDict = mainDict;
1273439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
1283439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
129bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        mWhiteListDictionary = WhitelistDictionary.init(context);
1303439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
1319f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        mAutoCorrection = new AutoCorrection();
13233e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka        initPool();
13333e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka    }
13433e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka
1353439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka    private void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key,
1363439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka            Dictionary dict) {
1373439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        final Dictionary oldDict = (dict == null)
1383439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka                ? dictionaries.remove(key)
1393439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka                : dictionaries.put(key, dict);
1403439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        if (oldDict != null && dict != oldDict) {
1413439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka            oldDict.close();
1423439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        }
1433439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka    }
1443439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka
145cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    public void resetMainDict(Context context, int dictionaryResId, Locale locale) {
1464250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager(
1474250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard                context, locale, dictionaryResId);
148cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mMainDict = newMainDict;
1493439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
1503439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
151cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
152cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
153979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private void initPool() {
154923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        for (int i = 0; i < mPrefMaxSuggestions; i++) {
155979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
156923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mStringPool.add(sb);
157923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
158923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
15934386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
1605ef096f5f601c759e8a4a888aaca91ac5ccd9974Tadashi G. Takaoka    public void setQuickFixesEnabled(boolean enabled) {
1615ef096f5f601c759e8a4a888aaca91ac5ccd9974Tadashi G. Takaoka        mQuickFixesEnabled = enabled;
162fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani    }
163fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani
164923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public int getCorrectionMode() {
165923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return mCorrectionMode;
166923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
16734386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
168923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void setCorrectionMode(int mode) {
169923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mCorrectionMode = mode;
170923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
171923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
172e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    public boolean hasMainDictionary() {
1734250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        return mMainDict != null;
174e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    }
175e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani
176bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public Map<String, Dictionary> getUnigramDictionaries() {
177bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        return mUnigramDictionaries;
178bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    }
179bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
180979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public int getApproxMaxWordLength() {
181979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return APPROX_MAX_WORD_LENGTH;
182979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
183979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
184923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
185923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
186f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard     * before the main dictionary, if set. This refers to the system-managed user dictionary.
187923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
188923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void setUserDictionary(Dictionary userDictionary) {
1893439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary);
190923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1912bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer
1922bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    /**
193699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard     * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove
194699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard     * the contacts dictionary by passing null to this method. In this case no contacts dictionary
195699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard     * won't be used.
1962bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer     */
197c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka    public void setContactsDictionary(Dictionary contactsDictionary) {
1983439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
1993439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
2002bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    }
201e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
202f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard    public void setUserUnigramDictionary(Dictionary userUnigramDictionary) {
203f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_UNIGRAM, userUnigramDictionary);
20434386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani    }
205923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
206979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void setUserBigramDictionary(Dictionary userBigramDictionary) {
2073439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_BIGRAM, userBigramDictionary);
208979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
209979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2101b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void setAutoCorrectionThreshold(double threshold) {
2111b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        mAutoCorrectionThreshold = threshold;
212b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda    }
213b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda
21414e427d5bb13d59d23fb317ef90a6c44ae279425satok    public boolean isAggressiveAutoCorrectionMode() {
21514e427d5bb13d59d23fb317ef90a6c44ae279425satok        return (mAutoCorrectionThreshold == 0);
21614e427d5bb13d59d23fb317ef90a6c44ae279425satok    }
21714e427d5bb13d59d23fb317ef90a6c44ae279425satok
218923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
219923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Number of suggestions to generate from the input key sequence. This has
220923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * to be a number between 1 and 100 (inclusive).
221923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param maxSuggestions
222923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @throws IllegalArgumentException if the number is out of range
223923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
224923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void setMaxSuggestions(int maxSuggestions) {
225923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (maxSuggestions < 1 || maxSuggestions > 100) {
226923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            throw new IllegalArgumentException("maxSuggestions must be between 1 and 100");
227923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
228923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPrefMaxSuggestions = maxSuggestions;
229e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        mScores = new int[mPrefMaxSuggestions];
230e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        mBigramScores = new int[PREF_MAX_BIGRAMS];
231979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        collectGarbage(mSuggestions, mPrefMaxSuggestions);
232923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        while (mStringPool.size() < mPrefMaxSuggestions) {
233979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
234923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mStringPool.add(sb);
235923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
236923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
23763fa90a7910d9f43f27a0bf9a6702f8fb44ce3e7Amith Yamasani
238923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
2397e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka     * Returns a object which represents suggested words that match the list of character codes
2407e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka     * passed in. This object contents will be overwritten the next time this function is called.
241979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * @param view a view for retrieving the context for AutoText
242979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * @param wordComposer contains what is currently being typed
243979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * @param prevWordForBigram previous word (used only for bigram)
2447e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka     * @return suggested words object.
245923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
2467e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    public SuggestedWords getSuggestions(View view, WordComposer wordComposer,
2479ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka            CharSequence prevWordForBigram) {
2489ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka        return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build();
2497e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    }
2507e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka
251bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) {
252bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        if (TextUtils.isEmpty(word) || !(all || first)) return word;
253bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final int wordLength = word.length();
254bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final int poolSize = mStringPool.size();
255bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final StringBuilder sb =
256bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
257bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        : new StringBuilder(getApproxMaxWordLength());
258bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        sb.setLength(0);
25935f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka        // TODO: Must pay attention to locale when changing case.
260bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        if (all) {
261bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            sb.append(word.toString().toUpperCase());
262bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        } else if (first) {
263bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            sb.append(Character.toUpperCase(word.charAt(0)));
264bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            if (wordLength > 1) {
265bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                sb.append(word.subSequence(1, wordLength));
266bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            }
267bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        }
268bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        return sb;
269bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    }
270bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
27189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    protected void addBigramToSuggestions(CharSequence bigram) {
27289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        final int poolSize = mStringPool.size();
27389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        final StringBuilder sb = poolSize > 0 ?
27489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                (StringBuilder) mStringPool.remove(poolSize - 1)
27589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        : new StringBuilder(getApproxMaxWordLength());
27689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        sb.setLength(0);
27789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        sb.append(bigram);
27889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        mSuggestions.add(sb);
27989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    }
28089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
2817e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
2827e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
2839ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka            CharSequence prevWordForBigram) {
284979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.onStartSuggestion(prevWordForBigram);
2859f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        mAutoCorrection.init();
2860b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
2870b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        mIsAllUpperCase = wordComposer.isAllUpperCase();
288979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        collectGarbage(mSuggestions, mPrefMaxSuggestions);
289e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        Arrays.fill(mScores, 0);
2901b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani
291923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Save a lowercase version of the original word
2929ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka        CharSequence typedWord = wordComposer.getTypedWord();
2939ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka        if (typedWord != null) {
2949ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka            final String typedWordString = typedWord.toString();
2959ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka            typedWord = typedWordString;
296979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // Treating USER_TYPED as UNIGRAM suggestion for logging now.
2979ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka            LatinImeLogger.onAddSuggestedWord(typedWordString, Suggest.DIC_USER_TYPED,
298979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    Dictionary.DataType.UNIGRAM);
299923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
300e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        mTypedWord = typedWord;
301979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
30289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (wordComposer.size() <= 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
303979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                || mCorrectionMode == CORRECTION_BASIC)) {
304979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // At first character typed, search only the bigrams
305e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            Arrays.fill(mBigramScores, 0);
306979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS);
307979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
308979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (!TextUtils.isEmpty(prevWordForBigram)) {
309979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
310e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa                if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) {
311979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    prevWordForBigram = lowerPrevWord;
312979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
313c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                for (final Dictionary dictionary : mBigramDictionaries.values()) {
314c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                    dictionary.getBigrams(wordComposer, prevWordForBigram, this);
315979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
31689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                if (TextUtils.isEmpty(typedWord)) {
31789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    // Nothing entered: return all bigrams for the previous word
31889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions);
31989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    for (int i = 0; i < insertCount; ++i) {
32089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        addBigramToSuggestions(mBigramSuggestions.get(i));
32189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    }
32289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                } else {
32389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    // Word entered: return only bigrams that match the first char of the typed word
32489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    final char currentChar = typedWord.charAt(0);
32535f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                    // TODO: Must pay attention to locale when changing case.
32689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    final char currentCharUpper = Character.toUpperCase(currentChar);
32789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    int count = 0;
32889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    final int bigramSuggestionSize = mBigramSuggestions.size();
32989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    for (int i = 0; i < bigramSuggestionSize; i++) {
33089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        final CharSequence bigramSuggestion = mBigramSuggestions.get(i);
33189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        final char bigramSuggestionFirstChar = bigramSuggestion.charAt(0);
33289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        if (bigramSuggestionFirstChar == currentChar
33389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                                || bigramSuggestionFirstChar == currentCharUpper) {
33489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                            addBigramToSuggestions(bigramSuggestion);
33589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                            if (++count > mPrefMaxSuggestions) break;
33689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        }
337979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    }
338979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
339979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
340979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
341979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else if (wordComposer.size() > 1) {
342979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // At second character typed, search the unigrams (scores being affected by bigrams)
343c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            for (final String key : mUnigramDictionaries.keySet()) {
344f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                // Skip UserUnigramDictionary and WhitelistDictionary to lookup
345f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
346c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                    continue;
347c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                final Dictionary dictionary = mUnigramDictionaries.get(key);
348c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                dictionary.getWords(wordComposer, this);
349923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
350923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
3519f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        CharSequence autoText = null;
3529f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        final String typedWordString = typedWord == null ? null : typedWord.toString();
3539ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka        if (typedWord != null) {
3549f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            // Apply quick fix only for the typed word.
3559f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            if (mQuickFixesEnabled) {
3569f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                final String lowerCaseTypedWord = typedWordString.toLowerCase();
357bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                CharSequence tempAutoText = capitalizeWord(
358bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        mIsAllUpperCase, mIsFirstCharCapitalized, AutoText.get(
359bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                                lowerCaseTypedWord, 0, lowerCaseTypedWord.length(), view));
360bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                // TODO: cleanup canAdd
361200ece79070750ba702a071908d990f8d1c41f02Ken Wakasa                // Is there an AutoText (also known as Quick Fixes) correction?
362200ece79070750ba702a071908d990f8d1c41f02Ken Wakasa                // Capitalize as needed
3639f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                boolean canAdd = tempAutoText != null;
364fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                // Is that correction already the current prediction (or original word)?
3659f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                canAdd &= !TextUtils.equals(tempAutoText, typedWord);
366fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                // Is that correction already the next predicted word?
3679f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                if (canAdd && mSuggestions.size() > 0 && mCorrectionMode != CORRECTION_BASIC) {
3689f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                    canAdd &= !TextUtils.equals(tempAutoText, mSuggestions.get(0));
369fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                }
370fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                if (canAdd) {
37182411d47ba7e8133ed2390c6920945e139a738cesatok                    if (DBG) {
37282411d47ba7e8133ed2390c6920945e139a738cesatok                        Log.d(TAG, "Auto corrected by AUTOTEXT.");
37382411d47ba7e8133ed2390c6920945e139a738cesatok                    }
3749f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                    autoText = tempAutoText;
375fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                }
376923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
377923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
3789f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
379bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized,
380bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                mWhiteListDictionary.getWhiteListedWord(typedWordString));
381bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
382bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer,
383e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode,
384bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                autoText, whitelistedWord);
3859f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
3869f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        if (autoText != null) {
3879f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            mSuggestions.add(0, autoText);
3889f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        }
3899f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
390bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        if (whitelistedWord != null) {
391bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            mSuggestions.add(0, whitelistedWord);
392bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        }
393bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
3949f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        if (typedWord != null) {
3959f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            mSuggestions.add(0, typedWordString);
3969f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        }
397bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        removeDupes();
3989f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
3998553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard        if (DBG) {
4009f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            double normalizedScore = mAutoCorrection.getNormalizedScore();
401e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            ArrayList<SuggestedWords.SuggestedWordInfo> scoreInfoList =
4028553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                    new ArrayList<SuggestedWords.SuggestedWordInfo>();
403e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
404e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            for (int i = 0; i < mScores.length; ++i) {
4058553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                if (normalizedScore > 0) {
406071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka                    final String scoreThreshold = String.format("%d (%4.2f)", mScores[i],
407071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka                            normalizedScore);
408e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    scoreInfoList.add(
409e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            new SuggestedWords.SuggestedWordInfo(scoreThreshold, false));
4108553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                    normalizedScore = 0.0;
4118553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                } else {
412e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    final String score = Integer.toString(mScores[i]);
413e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    scoreInfoList.add(new SuggestedWords.SuggestedWordInfo(score, false));
4148553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                }
4158553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard            }
416e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            for (int i = mScores.length; i < mSuggestions.size(); ++i) {
417e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
4188553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard            }
419e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            return new SuggestedWords.Builder().addWords(mSuggestions, scoreInfoList);
4208553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard        }
4219f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        return new SuggestedWords.Builder().addWords(mSuggestions, null);
422923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
423923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
424bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private void removeDupes() {
425bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        final ArrayList<CharSequence> suggestions = mSuggestions;
426bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        if (suggestions.size() < 2) return;
427bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        int i = 1;
428bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        // Don't cache suggestions.size(), since we may be removing items
429bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        while (i < suggestions.size()) {
430bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            final CharSequence cur = suggestions.get(i);
431bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            // Compare each candidate with each previous candidate
432bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            for (int j = 0; j < i; j++) {
433bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                CharSequence previous = suggestions.get(j);
434bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                if (TextUtils.equals(cur, previous)) {
435bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                    removeFromSuggestions(i);
436bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                    i--;
437bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                    break;
438bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                }
439bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            }
440bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            i++;
441bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        }
442bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    }
443bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani
444bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private void removeFromSuggestions(int index) {
445bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        CharSequence garbage = mSuggestions.remove(index);
446bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        if (garbage != null && garbage instanceof StringBuilder) {
447bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            mStringPool.add(garbage);
448bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        }
449bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    }
450bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani
4516f7218627eda110a8454053f8ecb7b80edfdc8cesatok    public boolean hasAutoCorrection() {
4529f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        return mAutoCorrection.hasAutoCorrection();
453923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
454923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
4555a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
456e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    public boolean addWord(final char[] word, final int offset, final int length, int score,
457979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            final int dicTypeId, final Dictionary.DataType dataType) {
458979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        Dictionary.DataType dataTypeForLog = dataType;
459e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final ArrayList<CharSequence> suggestions;
460e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final int[] sortedScores;
461e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final int prefMaxSuggestions;
462979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if(dataType == Dictionary.DataType.BIGRAM) {
463979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            suggestions = mBigramSuggestions;
464e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            sortedScores = mBigramScores;
465979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            prefMaxSuggestions = PREF_MAX_BIGRAMS;
466979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
467979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            suggestions = mSuggestions;
468e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            sortedScores = mScores;
469979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            prefMaxSuggestions = mPrefMaxSuggestions;
470979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
471979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
472923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int pos = 0;
473979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
474923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if it's the same word, only caps are different
475e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        if (Utils.equalsIgnoreCase(mTypedWord, word, offset, length)) {
476d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            // TODO: remove this surrounding if clause and move this logic to
477d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            // getSuggestedWordBuilder.
478d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            if (suggestions.size() > 0) {
479e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                final String currentHighestWord = suggestions.get(0).toString();
480d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // If the current highest word is also equal to typed word, we need to compare
481d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // frequency to determine the insertion position. This does not ensure strictly
482d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // correct ordering, but ensures the top score is on top which is enough for
483d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // removing duplicates correctly.
484e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                if (Utils.equalsIgnoreCase(currentHighestWord, word, offset, length)
485e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                        && score <= sortedScores[0]) {
486d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                    pos = 1;
487d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                }
488d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            }
489923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
490979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (dataType == Dictionary.DataType.UNIGRAM) {
491979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                // Check if the word was already added before (by bigram data)
492979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                int bigramSuggestion = searchBigramSuggestion(word,offset,length);
493979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if(bigramSuggestion >= 0) {
494979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    dataTypeForLog = Dictionary.DataType.BIGRAM;
495979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    // turn freq from bigram into multiplier specified above
496e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    double multiplier = (((double) mBigramScores[bigramSuggestion])
497979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            / MAXIMUM_BIGRAM_FREQUENCY)
498979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
499979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + BIGRAM_MULTIPLIER_MIN;
500979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    /* Log.d(TAG,"bigram num: " + bigramSuggestion
501979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + "  wordB: " + mBigramSuggestions.get(bigramSuggestion).toString()
502e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            + "  currentScore: " + score + "  bigramScore: "
503e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            + mBigramScores[bigramSuggestion]
504979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + "  multiplier: " + multiplier); */
505e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    score = (int)Math.round((score * multiplier));
506979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
507979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
508979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
509e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            // Check the last one's score and bail
510e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            if (sortedScores[prefMaxSuggestions - 1] >= score) return true;
511923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            while (pos < prefMaxSuggestions) {
512e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                if (sortedScores[pos] < score
513e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                        || (sortedScores[pos] == score && length < suggestions.get(pos).length())) {
514923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    break;
515923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                }
516923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                pos++;
517923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
518923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
519923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (pos >= prefMaxSuggestions) {
520923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
521923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
522979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
523e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
524e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        sortedScores[pos] = score;
525923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int poolSize = mStringPool.size();
526e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
527979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                : new StringBuilder(getApproxMaxWordLength());
528923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        sb.setLength(0);
52935f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka        // TODO: Must pay attention to locale when changing case.
5300b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        if (mIsAllUpperCase) {
5310b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa            sb.append(new String(word, offset, length).toUpperCase());
5320b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        } else if (mIsFirstCharCapitalized) {
533359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            sb.append(Character.toUpperCase(word[offset]));
534359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            if (length > 1) {
535359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani                sb.append(word, offset + 1, length - 1);
536359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            }
537359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani        } else {
538359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            sb.append(word, offset, length);
539359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani        }
540979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        suggestions.add(pos, sb);
541979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (suggestions.size() > prefMaxSuggestions) {
542979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            CharSequence garbage = suggestions.remove(prefMaxSuggestions);
543923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (garbage instanceof StringBuilder) {
544923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mStringPool.add(garbage);
545923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
546979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
547979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
548923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
549923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
550923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
551923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
552979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private int searchBigramSuggestion(final char[] word, final int offset, final int length) {
553979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // TODO This is almost O(n^2). Might need fix.
554979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // search whether the word appeared in bigram data
555979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        int bigramSuggestSize = mBigramSuggestions.size();
556979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        for(int i = 0; i < bigramSuggestSize; i++) {
557979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if(mBigramSuggestions.get(i).length() == length) {
558979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                boolean chk = true;
559979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                for(int j = 0; j < length; j++) {
560979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    if(mBigramSuggestions.get(i).charAt(j) != word[offset+j]) {
561979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                        chk = false;
562979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                        break;
563979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    }
564979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
565979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if(chk) return i;
566979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
567979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
568979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
569979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return -1;
570979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
571979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
572979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private void collectGarbage(ArrayList<CharSequence> suggestions, int prefMaxSuggestions) {
573923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int poolSize = mStringPool.size();
574979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        int garbageSize = suggestions.size();
575979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        while (poolSize < prefMaxSuggestions && garbageSize > 0) {
576979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            CharSequence garbage = suggestions.get(garbageSize - 1);
577923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (garbage != null && garbage instanceof StringBuilder) {
578923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mStringPool.add(garbage);
579923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                poolSize++;
580923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
581923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            garbageSize--;
582923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
583979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (poolSize == prefMaxSuggestions + 1) {
584923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            Log.w("Suggest", "String pool got too big: " + poolSize);
585923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
586979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        suggestions.clear();
587923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
58836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
58936fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    public void close() {
590c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        final Set<Dictionary> dictionaries = new HashSet<Dictionary>();
591c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        dictionaries.addAll(mUnigramDictionaries.values());
592c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        dictionaries.addAll(mBigramDictionaries.values());
593c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        for (final Dictionary dictionary : dictionaries) {
594c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            dictionary.close();
59536fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani        }
596c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        mMainDict = null;
59736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
598923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
599