Home | History | Annotate | Download | only in latin
      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 
     17 package com.android.inputmethod.latin;
     18 
     19 import android.content.Context;
     20 import android.text.TextUtils;
     21 import android.util.Log;
     22 import com.android.inputmethod.latin.Suggest;
     23 import com.android.inputmethod.latin.UserBigramDictionary;
     24 import com.android.inputmethod.latin.WordComposer;
     25 
     26 import java.io.IOException;
     27 import java.io.InputStream;
     28 import java.nio.ByteBuffer;
     29 import java.nio.ByteOrder;
     30 import java.nio.channels.Channels;
     31 import java.util.List;
     32 import java.util.Locale;
     33 import java.util.StringTokenizer;
     34 
     35 public 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