Suggest.java revision 699094f9b6e0a4621e8b3cfab70b59c0c7c086bb
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
63979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int DIC_USER_TYPED = 0;
64979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int DIC_MAIN = 1;
65979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int DIC_USER = 2;
66979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int DIC_AUTO = 3;
67979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int DIC_CONTACTS = 4;
68979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
69979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public static final int DIC_TYPE_LAST_ID = 4;
7034386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
71bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public static final String DICT_KEY_MAIN = "main";
72bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public static final String DICT_KEY_CONTACTS = "contacts";
73bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public static final String DICT_KEY_AUTO = "auto";
74bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public static final String DICT_KEY_USER = "user";
75bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public static final String DICT_KEY_USER_BIGRAM = "user_bigram";
76bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public static final String DICT_KEY_WHITELIST ="whitelist";
77bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
788553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard    private static final boolean DBG = LatinImeLogger.sDBG;
7982411d47ba7e8133ed2390c6920945e139a738cesatok
809f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok    private AutoCorrection mAutoCorrection;
819f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
824250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard    private Dictionary mMainDict;
83bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    private WhitelistDictionary mWhiteListDictionary;
84c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka    private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>();
85c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka    private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>();
86979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
87923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mPrefMaxSuggestions = 12;
8834386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
89979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private static final int PREF_MAX_BIGRAMS = 60;
90979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
915ef096f5f601c759e8a4a888aaca91ac5ccd9974Tadashi G. Takaoka    private boolean mQuickFixesEnabled;
92fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani
931b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private double mAutoCorrectionThreshold;
94e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    private int[] mScores = new int[mPrefMaxSuggestions];
95e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    private int[] mBigramScores = new int[PREF_MAX_BIGRAMS];
96979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
97bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
98979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    ArrayList<CharSequence> mBigramSuggestions  = new ArrayList<CharSequence>();
99bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
100e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    private CharSequence mTypedWord;
1010b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
1020b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    // TODO: Remove these member variables by passing more context to addWord() callback method
1030b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
1040b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsAllUpperCase;
105923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
106923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mCorrectionMode = CORRECTION_BASIC;
107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
108cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    public Suggest(Context context, int dictionaryResId, Locale locale) {
1094250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        init(context, DictionaryFactory.createDictionaryFromManager(context, locale,
110cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard                dictionaryResId));
111979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
112979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
113c899038eee5c01d520a2707cca01ee093a674d05Jean Chalard    /* package for test */ Suggest(Context context, File dictionary, long startOffset, long length,
114c899038eee5c01d520a2707cca01ee093a674d05Jean Chalard            Flag[] flagArray) {
1154250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        init(null, DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset,
1164250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard                length, flagArray));
1179f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok    }
1189f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
1194250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard    private void init(Context context, Dictionary mainDict) {
120c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        if (mainDict != null) {
121c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mMainDict = mainDict;
122c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mUnigramDictionaries.put(DICT_KEY_MAIN, mainDict);
123c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mBigramDictionaries.put(DICT_KEY_MAIN, mainDict);
124c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        }
125bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        mWhiteListDictionary = WhitelistDictionary.init(context);
126bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        if (mWhiteListDictionary != null) {
127bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            mUnigramDictionaries.put(DICT_KEY_WHITELIST, mWhiteListDictionary);
128bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        }
1299f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        mAutoCorrection = new AutoCorrection();
13033e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka        initPool();
13133e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka    }
13233e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka
133cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    public void resetMainDict(Context context, int dictionaryResId, Locale locale) {
1344250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager(
1354250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard                context, locale, dictionaryResId);
136cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mMainDict = newMainDict;
137cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        if (null == newMainDict) {
138cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            mUnigramDictionaries.remove(DICT_KEY_MAIN);
139cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            mBigramDictionaries.remove(DICT_KEY_MAIN);
140cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        } else {
141cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            mUnigramDictionaries.put(DICT_KEY_MAIN, newMainDict);
142cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            mBigramDictionaries.put(DICT_KEY_MAIN, newMainDict);
143cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        }
144cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
145cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
146979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private void initPool() {
147923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        for (int i = 0; i < mPrefMaxSuggestions; i++) {
148979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
149923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mStringPool.add(sb);
150923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
151923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
15234386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
1535ef096f5f601c759e8a4a888aaca91ac5ccd9974Tadashi G. Takaoka    public void setQuickFixesEnabled(boolean enabled) {
1545ef096f5f601c759e8a4a888aaca91ac5ccd9974Tadashi G. Takaoka        mQuickFixesEnabled = enabled;
155fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani    }
156fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani
157923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public int getCorrectionMode() {
158923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return mCorrectionMode;
159923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
16034386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani
161923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void setCorrectionMode(int mode) {
162923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mCorrectionMode = mode;
163923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
164923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
165e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    public boolean hasMainDictionary() {
1664250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        return mMainDict != null;
167e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    }
168e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani
169bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    public Map<String, Dictionary> getUnigramDictionaries() {
170bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        return mUnigramDictionaries;
171bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    }
172bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
173979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public int getApproxMaxWordLength() {
174979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return APPROX_MAX_WORD_LENGTH;
175979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
176979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
177923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
178923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
179923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * before the main dictionary, if set.
180923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
181923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void setUserDictionary(Dictionary userDictionary) {
182c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        if (userDictionary != null)
183c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mUnigramDictionaries.put(DICT_KEY_USER, userDictionary);
184923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1852bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer
1862bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    /**
187699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard     * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove
188699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard     * the contacts dictionary by passing null to this method. In this case no contacts dictionary
189699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard     * won't be used.
1902bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer     */
191c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka    public void setContactsDictionary(Dictionary contactsDictionary) {
192c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        if (contactsDictionary != null) {
193c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mUnigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary);
194c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mBigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary);
195699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard        } else {
196699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard            mUnigramDictionaries.remove(DICT_KEY_CONTACTS);
197699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard            mBigramDictionaries.remove(DICT_KEY_CONTACTS);
198c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        }
1992bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    }
200e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
20134386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani    public void setAutoDictionary(Dictionary autoDictionary) {
202c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        if (autoDictionary != null)
203c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mUnigramDictionaries.put(DICT_KEY_AUTO, autoDictionary);
20434386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani    }
205923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
206979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void setUserBigramDictionary(Dictionary userBigramDictionary) {
207c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        if (userBigramDictionary != null)
208c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            mBigramDictionaries.put(DICT_KEY_USER_BIGRAM, userBigramDictionary);
209979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
210979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2111b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void setAutoCorrectionThreshold(double threshold) {
2121b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        mAutoCorrectionThreshold = threshold;
213b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda    }
214b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda
21514e427d5bb13d59d23fb317ef90a6c44ae279425satok    public boolean isAggressiveAutoCorrectionMode() {
21614e427d5bb13d59d23fb317ef90a6c44ae279425satok        return (mAutoCorrectionThreshold == 0);
21714e427d5bb13d59d23fb317ef90a6c44ae279425satok    }
21814e427d5bb13d59d23fb317ef90a6c44ae279425satok
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);
233923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        while (mStringPool.size() < mPrefMaxSuggestions) {
234979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
235923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mStringPool.add(sb);
236923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
237923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
23863fa90a7910d9f43f27a0bf9a6702f8fb44ce3e7Amith Yamasani
239923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
2407e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka     * Returns a object which represents suggested words that match the list of character codes
2417e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka     * passed in. This object contents will be overwritten the next time this function is called.
242979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * @param view a view for retrieving the context for AutoText
243979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * @param wordComposer contains what is currently being typed
244979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * @param prevWordForBigram previous word (used only for bigram)
2457e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka     * @return suggested words object.
246923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
2477e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    public SuggestedWords getSuggestions(View view, WordComposer wordComposer,
2489ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka            CharSequence prevWordForBigram) {
2499ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka        return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build();
2507e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    }
2517e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka
252bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) {
253bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        if (TextUtils.isEmpty(word) || !(all || first)) return word;
254bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final int wordLength = word.length();
255bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final int poolSize = mStringPool.size();
256bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final StringBuilder sb =
257bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
258bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        : new StringBuilder(getApproxMaxWordLength());
259bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        sb.setLength(0);
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);
32589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    final char currentCharUpper = Character.toUpperCase(currentChar);
32689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    int count = 0;
32789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    final int bigramSuggestionSize = mBigramSuggestions.size();
32889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    for (int i = 0; i < bigramSuggestionSize; i++) {
32989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        final CharSequence bigramSuggestion = mBigramSuggestions.get(i);
33089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        final char bigramSuggestionFirstChar = bigramSuggestion.charAt(0);
33189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        if (bigramSuggestionFirstChar == currentChar
33289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                                || bigramSuggestionFirstChar == currentCharUpper) {
33389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                            addBigramToSuggestions(bigramSuggestion);
33489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                            if (++count > mPrefMaxSuggestions) break;
33589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                        }
336979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    }
337979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
338979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
339979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
340979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else if (wordComposer.size() > 1) {
341979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // At second character typed, search the unigrams (scores being affected by bigrams)
342c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            for (final String key : mUnigramDictionaries.keySet()) {
343bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                // Skip AutoDictionary and WhitelistDictionary to lookup
344bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                if (key.equals(DICT_KEY_AUTO) || key.equals(DICT_KEY_WHITELIST))
345c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                    continue;
346c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                final Dictionary dictionary = mUnigramDictionaries.get(key);
347c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka                dictionary.getWords(wordComposer, this);
348923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
349923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
3509f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        CharSequence autoText = null;
3519f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        final String typedWordString = typedWord == null ? null : typedWord.toString();
3529ecad8c2e8571ece6f3f7fbb19ceda5be7866cf0Tadashi G. Takaoka        if (typedWord != null) {
3539f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            // Apply quick fix only for the typed word.
3549f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            if (mQuickFixesEnabled) {
3559f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                final String lowerCaseTypedWord = typedWordString.toLowerCase();
356bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                CharSequence tempAutoText = capitalizeWord(
357bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        mIsAllUpperCase, mIsFirstCharCapitalized, AutoText.get(
358bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                                lowerCaseTypedWord, 0, lowerCaseTypedWord.length(), view));
359bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                // TODO: cleanup canAdd
360200ece79070750ba702a071908d990f8d1c41f02Ken Wakasa                // Is there an AutoText (also known as Quick Fixes) correction?
361200ece79070750ba702a071908d990f8d1c41f02Ken Wakasa                // Capitalize as needed
3629f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                boolean canAdd = tempAutoText != null;
363fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                // Is that correction already the current prediction (or original word)?
3649f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                canAdd &= !TextUtils.equals(tempAutoText, typedWord);
365fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                // Is that correction already the next predicted word?
3669f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                if (canAdd && mSuggestions.size() > 0 && mCorrectionMode != CORRECTION_BASIC) {
3679f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                    canAdd &= !TextUtils.equals(tempAutoText, mSuggestions.get(0));
368fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                }
369fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                if (canAdd) {
37082411d47ba7e8133ed2390c6920945e139a738cesatok                    if (DBG) {
37182411d47ba7e8133ed2390c6920945e139a738cesatok                        Log.d(TAG, "Auto corrected by AUTOTEXT.");
37282411d47ba7e8133ed2390c6920945e139a738cesatok                    }
3739f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok                    autoText = tempAutoText;
374fac5dcb5fee7c6416af4796f90a8272b7712890eAmith Yamasani                }
375923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
376923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
3779f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
378bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized,
379bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                mWhiteListDictionary.getWhiteListedWord(typedWordString));
380bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
381bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer,
382e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode,
383bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                autoText, whitelistedWord);
3849f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
3859f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        if (autoText != null) {
3869f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            mSuggestions.add(0, autoText);
3879f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        }
3889f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
389bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        if (whitelistedWord != null) {
390bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            mSuggestions.add(0, whitelistedWord);
391bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        }
392bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
3939f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        if (typedWord != null) {
3949f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            mSuggestions.add(0, typedWordString);
3959f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        }
396bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        removeDupes();
3979f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok
3988553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard        if (DBG) {
3999f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok            double normalizedScore = mAutoCorrection.getNormalizedScore();
400e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            ArrayList<SuggestedWords.SuggestedWordInfo> scoreInfoList =
4018553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                    new ArrayList<SuggestedWords.SuggestedWordInfo>();
402e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
403e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            for (int i = 0; i < mScores.length; ++i) {
4048553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                if (normalizedScore > 0) {
405071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka                    final String scoreThreshold = String.format("%d (%4.2f)", mScores[i],
406071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka                            normalizedScore);
407e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    scoreInfoList.add(
408e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            new SuggestedWords.SuggestedWordInfo(scoreThreshold, false));
4098553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                    normalizedScore = 0.0;
4108553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                } else {
411e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    final String score = Integer.toString(mScores[i]);
412e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    scoreInfoList.add(new SuggestedWords.SuggestedWordInfo(score, false));
4138553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard                }
4148553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard            }
415e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            for (int i = mScores.length; i < mSuggestions.size(); ++i) {
416e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
4178553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard            }
418e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            return new SuggestedWords.Builder().addWords(mSuggestions, scoreInfoList);
4198553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard        }
4209f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        return new SuggestedWords.Builder().addWords(mSuggestions, null);
421923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
422923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
423bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private void removeDupes() {
424bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        final ArrayList<CharSequence> suggestions = mSuggestions;
425bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        if (suggestions.size() < 2) return;
426bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        int i = 1;
427bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        // Don't cache suggestions.size(), since we may be removing items
428bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        while (i < suggestions.size()) {
429bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            final CharSequence cur = suggestions.get(i);
430bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            // Compare each candidate with each previous candidate
431bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            for (int j = 0; j < i; j++) {
432bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                CharSequence previous = suggestions.get(j);
433bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                if (TextUtils.equals(cur, previous)) {
434bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                    removeFromSuggestions(i);
435bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                    i--;
436bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                    break;
437bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani                }
438bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            }
439bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            i++;
440bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        }
441bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    }
442bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani
443bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    private void removeFromSuggestions(int index) {
444bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        CharSequence garbage = mSuggestions.remove(index);
445bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        if (garbage != null && garbage instanceof StringBuilder) {
446bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani            mStringPool.add(garbage);
447bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani        }
448bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani    }
449bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani
4506f7218627eda110a8454053f8ecb7b80edfdc8cesatok    public boolean hasAutoCorrection() {
4519f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        return mAutoCorrection.hasAutoCorrection();
452923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
453923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
4545a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
455e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka    public boolean addWord(final char[] word, final int offset, final int length, int score,
456979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            final int dicTypeId, final Dictionary.DataType dataType) {
457979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        Dictionary.DataType dataTypeForLog = dataType;
458e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final ArrayList<CharSequence> suggestions;
459e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final int[] sortedScores;
460e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        final int prefMaxSuggestions;
461979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if(dataType == Dictionary.DataType.BIGRAM) {
462979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            suggestions = mBigramSuggestions;
463e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            sortedScores = mBigramScores;
464979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            prefMaxSuggestions = PREF_MAX_BIGRAMS;
465979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
466979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            suggestions = mSuggestions;
467e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            sortedScores = mScores;
468979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            prefMaxSuggestions = mPrefMaxSuggestions;
469979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
470979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
471923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int pos = 0;
472979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
473923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if it's the same word, only caps are different
474e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        if (Utils.equalsIgnoreCase(mTypedWord, word, offset, length)) {
475d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            // TODO: remove this surrounding if clause and move this logic to
476d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            // getSuggestedWordBuilder.
477d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            if (suggestions.size() > 0) {
478e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                final String currentHighestWord = suggestions.get(0).toString();
479d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // If the current highest word is also equal to typed word, we need to compare
480d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // frequency to determine the insertion position. This does not ensure strictly
481d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // correct ordering, but ensures the top score is on top which is enough for
482d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                // removing duplicates correctly.
483e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                if (Utils.equalsIgnoreCase(currentHighestWord, word, offset, length)
484e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                        && score <= sortedScores[0]) {
485d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                    pos = 1;
486d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard                }
487d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard            }
488923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
489979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (dataType == Dictionary.DataType.UNIGRAM) {
490979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                // Check if the word was already added before (by bigram data)
491979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                int bigramSuggestion = searchBigramSuggestion(word,offset,length);
492979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if(bigramSuggestion >= 0) {
493979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    dataTypeForLog = Dictionary.DataType.BIGRAM;
494979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    // turn freq from bigram into multiplier specified above
495e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    double multiplier = (((double) mBigramScores[bigramSuggestion])
496979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            / MAXIMUM_BIGRAM_FREQUENCY)
497979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
498979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + BIGRAM_MULTIPLIER_MIN;
499979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    /* Log.d(TAG,"bigram num: " + bigramSuggestion
500979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + "  wordB: " + mBigramSuggestions.get(bigramSuggestion).toString()
501e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            + "  currentScore: " + score + "  bigramScore: "
502e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                            + mBigramScores[bigramSuggestion]
503979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                            + "  multiplier: " + multiplier); */
504e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                    score = (int)Math.round((score * multiplier));
505979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
506979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
507979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
508e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            // Check the last one's score and bail
509e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka            if (sortedScores[prefMaxSuggestions - 1] >= score) return true;
510923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            while (pos < prefMaxSuggestions) {
511e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                if (sortedScores[pos] < score
512e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka                        || (sortedScores[pos] == score && length < suggestions.get(pos).length())) {
513923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    break;
514923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                }
515923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                pos++;
516923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
517923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
518923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (pos >= prefMaxSuggestions) {
519923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
520923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
521979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
522e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
523e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka        sortedScores[pos] = score;
524923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int poolSize = mStringPool.size();
525e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
526979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                : new StringBuilder(getApproxMaxWordLength());
527923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        sb.setLength(0);
5280b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        if (mIsAllUpperCase) {
5290b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa            sb.append(new String(word, offset, length).toUpperCase());
5300b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        } else if (mIsFirstCharCapitalized) {
531359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            sb.append(Character.toUpperCase(word[offset]));
532359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            if (length > 1) {
533359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani                sb.append(word, offset + 1, length - 1);
534359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            }
535359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani        } else {
536359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani            sb.append(word, offset, length);
537359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani        }
538979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        suggestions.add(pos, sb);
539979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (suggestions.size() > prefMaxSuggestions) {
540979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            CharSequence garbage = suggestions.remove(prefMaxSuggestions);
541923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (garbage instanceof StringBuilder) {
542923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mStringPool.add(garbage);
543923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
544979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
545979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
546923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
547923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
548923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
549923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
550979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private int searchBigramSuggestion(final char[] word, final int offset, final int length) {
551979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // TODO This is almost O(n^2). Might need fix.
552979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // search whether the word appeared in bigram data
553979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        int bigramSuggestSize = mBigramSuggestions.size();
554979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        for(int i = 0; i < bigramSuggestSize; i++) {
555979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if(mBigramSuggestions.get(i).length() == length) {
556979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                boolean chk = true;
557979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                for(int j = 0; j < length; j++) {
558979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    if(mBigramSuggestions.get(i).charAt(j) != word[offset+j]) {
559979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                        chk = false;
560979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                        break;
561979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    }
562979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
563979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if(chk) return i;
564979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
565979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
566979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
567979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return -1;
568979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
569979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
570979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private void collectGarbage(ArrayList<CharSequence> suggestions, int prefMaxSuggestions) {
571923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int poolSize = mStringPool.size();
572979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        int garbageSize = suggestions.size();
573979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        while (poolSize < prefMaxSuggestions && garbageSize > 0) {
574979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            CharSequence garbage = suggestions.get(garbageSize - 1);
575923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (garbage != null && garbage instanceof StringBuilder) {
576923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mStringPool.add(garbage);
577923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                poolSize++;
578923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
579923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            garbageSize--;
580923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
581979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (poolSize == prefMaxSuggestions + 1) {
582923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            Log.w("Suggest", "String pool got too big: " + poolSize);
583923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
584979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        suggestions.clear();
585923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
58636fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
58736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    public void close() {
588c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        final Set<Dictionary> dictionaries = new HashSet<Dictionary>();
589c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        dictionaries.addAll(mUnigramDictionaries.values());
590c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        dictionaries.addAll(mBigramDictionaries.values());
591c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        for (final Dictionary dictionary : dictionaries) {
592c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka            dictionary.close();
59336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani        }
594c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka        mMainDict = null;
59536fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
596923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
597