1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.inputmethod.latin; 18 19import android.content.Context; 20import android.text.TextUtils; 21import android.util.Log; 22import com.android.inputmethod.latin.Suggest; 23import com.android.inputmethod.latin.UserBigramDictionary; 24import com.android.inputmethod.latin.WordComposer; 25 26import java.io.IOException; 27import java.io.InputStream; 28import java.nio.ByteBuffer; 29import java.nio.ByteOrder; 30import java.nio.channels.Channels; 31import java.util.List; 32import java.util.Locale; 33import java.util.StringTokenizer; 34 35public class SuggestHelper { 36 private Suggest mSuggest; 37 private UserBigramDictionary mUserBigram; 38 private final String TAG; 39 40 /** Uses main dictionary only **/ 41 public SuggestHelper(String tag, Context context, int[] resId) { 42 TAG = tag; 43 InputStream[] is = null; 44 try { 45 // merging separated dictionary into one if dictionary is separated 46 int total = 0; 47 is = new InputStream[resId.length]; 48 for (int i = 0; i < resId.length; i++) { 49 is[i] = context.getResources().openRawResource(resId[i]); 50 total += is[i].available(); 51 } 52 53 ByteBuffer byteBuffer = 54 ByteBuffer.allocateDirect(total).order(ByteOrder.nativeOrder()); 55 int got = 0; 56 for (int i = 0; i < resId.length; i++) { 57 got += Channels.newChannel(is[i]).read(byteBuffer); 58 } 59 if (got != total) { 60 Log.w(TAG, "Read " + got + " bytes, expected " + total); 61 } else { 62 mSuggest = new Suggest(context, byteBuffer); 63 Log.i(TAG, "Created mSuggest " + total + " bytes"); 64 } 65 } catch (IOException e) { 66 Log.w(TAG, "No available memory for binary dictionary"); 67 } finally { 68 try { 69 if (is != null) { 70 for (int i = 0; i < is.length; i++) { 71 is[i].close(); 72 } 73 } 74 } catch (IOException e) { 75 Log.w(TAG, "Failed to close input stream"); 76 } 77 } 78 mSuggest.setAutoTextEnabled(false); 79 mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM); 80 } 81 82 /** Uses both main dictionary and user-bigram dictionary **/ 83 public SuggestHelper(String tag, Context context, int[] resId, int userBigramMax, 84 int userBigramDelete) { 85 this(tag, context, resId); 86 mUserBigram = new UserBigramDictionary(context, null, Locale.US.toString(), 87 Suggest.DIC_USER); 88 mUserBigram.setDatabaseMax(userBigramMax); 89 mUserBigram.setDatabaseDelete(userBigramDelete); 90 mSuggest.setUserBigramDictionary(mUserBigram); 91 } 92 93 void changeUserBigramLocale(Context context, Locale locale) { 94 if (mUserBigram != null) { 95 flushUserBigrams(); 96 mUserBigram.close(); 97 mUserBigram = new UserBigramDictionary(context, null, locale.toString(), 98 Suggest.DIC_USER); 99 mSuggest.setUserBigramDictionary(mUserBigram); 100 } 101 } 102 103 private WordComposer createWordComposer(CharSequence s) { 104 WordComposer word = new WordComposer(); 105 for (int i = 0; i < s.length(); i++) { 106 final char c = s.charAt(i); 107 int[] codes; 108 // If it's not a lowercase letter, don't find adjacent letters 109 if (c < 'a' || c > 'z') { 110 codes = new int[] { c }; 111 } else { 112 codes = adjacents[c - 'a']; 113 } 114 word.add(c, codes); 115 } 116 return word; 117 } 118 119 private void showList(String title, List<CharSequence> suggestions) { 120 Log.i(TAG, title); 121 for (int i = 0; i < suggestions.size(); i++) { 122 Log.i(title, suggestions.get(i) + ", "); 123 } 124 } 125 126 private boolean isDefaultSuggestion(List<CharSequence> suggestions, CharSequence word) { 127 // Check if either the word is what you typed or the first alternative 128 return suggestions.size() > 0 && 129 (/*TextUtils.equals(suggestions.get(0), word) || */ 130 (suggestions.size() > 1 && TextUtils.equals(suggestions.get(1), word))); 131 } 132 133 boolean isDefaultSuggestion(CharSequence typed, CharSequence expected) { 134 WordComposer word = createWordComposer(typed); 135 List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, null); 136 return isDefaultSuggestion(suggestions, expected); 137 } 138 139 boolean isDefaultCorrection(CharSequence typed, CharSequence expected) { 140 WordComposer word = createWordComposer(typed); 141 List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, null); 142 return isDefaultSuggestion(suggestions, expected) && mSuggest.hasMinimalCorrection(); 143 } 144 145 boolean isASuggestion(CharSequence typed, CharSequence expected) { 146 WordComposer word = createWordComposer(typed); 147 List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, null); 148 for (int i = 1; i < suggestions.size(); i++) { 149 if (TextUtils.equals(suggestions.get(i), expected)) return true; 150 } 151 return false; 152 } 153 154 private void getBigramSuggestions(CharSequence previous, CharSequence typed) { 155 if (!TextUtils.isEmpty(previous) && (typed.length() > 1)) { 156 WordComposer firstChar = createWordComposer(Character.toString(typed.charAt(0))); 157 mSuggest.getSuggestions(null, firstChar, false, previous); 158 } 159 } 160 161 boolean isDefaultNextSuggestion(CharSequence previous, CharSequence typed, 162 CharSequence expected) { 163 WordComposer word = createWordComposer(typed); 164 getBigramSuggestions(previous, typed); 165 List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, previous); 166 return isDefaultSuggestion(suggestions, expected); 167 } 168 169 boolean isDefaultNextCorrection(CharSequence previous, CharSequence typed, 170 CharSequence expected) { 171 WordComposer word = createWordComposer(typed); 172 getBigramSuggestions(previous, typed); 173 List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, previous); 174 return isDefaultSuggestion(suggestions, expected) && mSuggest.hasMinimalCorrection(); 175 } 176 177 boolean isASuggestion(CharSequence previous, CharSequence typed, 178 CharSequence expected) { 179 WordComposer word = createWordComposer(typed); 180 getBigramSuggestions(previous, typed); 181 List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, previous); 182 for (int i = 1; i < suggestions.size(); i++) { 183 if (TextUtils.equals(suggestions.get(i), expected)) return true; 184 } 185 return false; 186 } 187 188 boolean isValid(CharSequence typed) { 189 return mSuggest.isValidWord(typed); 190 } 191 192 boolean isUserBigramSuggestion(CharSequence previous, char typed, 193 CharSequence expected) { 194 WordComposer word = createWordComposer(Character.toString(typed)); 195 196 if (mUserBigram == null) return false; 197 198 flushUserBigrams(); 199 if (!TextUtils.isEmpty(previous) && !TextUtils.isEmpty(Character.toString(typed))) { 200 WordComposer firstChar = createWordComposer(Character.toString(typed)); 201 mSuggest.getSuggestions(null, firstChar, false, previous); 202 boolean reloading = mUserBigram.reloadDictionaryIfRequired(); 203 if (reloading) mUserBigram.waitForDictionaryLoading(); 204 mUserBigram.getBigrams(firstChar, previous, mSuggest, null); 205 } 206 207 List<CharSequence> suggestions = mSuggest.mBigramSuggestions; 208 for (int i = 0; i < suggestions.size(); i++) { 209 if (TextUtils.equals(suggestions.get(i), expected)) return true; 210 } 211 212 return false; 213 } 214 215 void addToUserBigram(String sentence) { 216 StringTokenizer st = new StringTokenizer(sentence); 217 String previous = null; 218 while (st.hasMoreTokens()) { 219 String current = st.nextToken(); 220 if (previous != null) { 221 addToUserBigram(new String[] {previous, current}); 222 } 223 previous = current; 224 } 225 } 226 227 void addToUserBigram(String[] pair) { 228 if (mUserBigram != null && pair.length == 2) { 229 mUserBigram.addBigrams(pair[0], pair[1]); 230 } 231 } 232 233 void flushUserBigrams() { 234 if (mUserBigram != null) { 235 mUserBigram.flushPendingWrites(); 236 mUserBigram.waitUntilUpdateDBDone(); 237 } 238 } 239 240 final int[][] adjacents = { 241 {'a','s','w','q',-1}, 242 {'b','h','v','n','g','j',-1}, 243 {'c','v','f','x','g',}, 244 {'d','f','r','e','s','x',-1}, 245 {'e','w','r','s','d',-1}, 246 {'f','g','d','c','t','r',-1}, 247 {'g','h','f','y','t','v',-1}, 248 {'h','j','u','g','b','y',-1}, 249 {'i','o','u','k',-1}, 250 {'j','k','i','h','u','n',-1}, 251 {'k','l','o','j','i','m',-1}, 252 {'l','k','o','p',-1}, 253 {'m','k','n','l',-1}, 254 {'n','m','j','k','b',-1}, 255 {'o','p','i','l',-1}, 256 {'p','o',-1}, 257 {'q','w',-1}, 258 {'r','t','e','f',-1}, 259 {'s','d','e','w','a','z',-1}, 260 {'t','y','r',-1}, 261 {'u','y','i','h','j',-1}, 262 {'v','b','g','c','h',-1}, 263 {'w','e','q',-1}, 264 {'x','c','d','z','f',-1}, 265 {'y','u','t','h','g',-1}, 266 {'z','s','x','a','d',-1}, 267 }; 268} 269