AndroidSpellCheckerService.java revision 5bcf8ee66ceb38675a6b70fefcb574978e0fae92
1/* 2 * Copyright (C) 2011 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.spellcheck; 18 19import android.content.res.Resources; 20import android.service.textservice.SpellCheckerService; 21import android.service.textservice.SpellCheckerService.Session; 22import android.util.Log; 23import android.view.textservice.SuggestionsInfo; 24import android.view.textservice.TextInfo; 25 26import com.android.inputmethod.compat.ArraysCompatUtils; 27import com.android.inputmethod.keyboard.Key; 28import com.android.inputmethod.keyboard.ProximityInfo; 29import com.android.inputmethod.latin.Dictionary; 30import com.android.inputmethod.latin.Dictionary.DataType; 31import com.android.inputmethod.latin.Dictionary.WordCallback; 32import com.android.inputmethod.latin.DictionaryFactory; 33import com.android.inputmethod.latin.Utils; 34import com.android.inputmethod.latin.WordComposer; 35 36import java.util.Collections; 37import java.util.List; 38import java.util.LinkedList; 39import java.util.Locale; 40import java.util.Map; 41import java.util.TreeMap; 42 43/** 44 * Service for spell checking, using LatinIME's dictionaries and mechanisms. 45 */ 46public class AndroidSpellCheckerService extends SpellCheckerService { 47 private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); 48 private static final boolean DBG = true; 49 50 private final static String[] emptyArray = new String[0]; 51 private final ProximityInfo mProximityInfo = ProximityInfo.getDummyProximityInfo(); 52 private final Map<String, Dictionary> mDictionaries = 53 Collections.synchronizedMap(new TreeMap<String, Dictionary>()); 54 55 @Override 56 public Session createSession() { 57 return new AndroidSpellCheckerSession(); 58 } 59 60 private static class SuggestionsGatherer implements WordCallback { 61 private final int DEFAULT_SUGGESTION_LENGTH = 16; 62 private final String[] mSuggestions; 63 private final int[] mScores; 64 private final int mMaxLength; 65 private int mLength = 0; 66 67 SuggestionsGatherer(final int maxLength) { 68 mMaxLength = maxLength; 69 mSuggestions = new String[mMaxLength]; 70 mScores = new int[mMaxLength]; 71 } 72 73 @Override 74 synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score, 75 int dicTypeId, DataType dataType) { 76 final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score); 77 // binarySearch returns the index if the element exists, and -<insertion index> - 1 78 // if it doesn't. See documentation for binarySearch. 79 final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; 80 81 if (mLength < mMaxLength) { 82 final int copyLen = mLength - insertIndex; 83 ++mLength; 84 System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen); 85 System.arraycopy(mSuggestions, insertIndex, mSuggestions, insertIndex + 1, copyLen); 86 } else { 87 if (insertIndex == 0) return true; 88 System.arraycopy(mScores, 1, mScores, 0, insertIndex); 89 System.arraycopy(mSuggestions, 1, mSuggestions, 0, insertIndex); 90 } 91 mScores[insertIndex] = score; 92 mSuggestions[insertIndex] = new String(word, wordOffset, wordLength); 93 94 return true; 95 } 96 97 public String[] getGatheredSuggestions() { 98 if (0 == mLength) return null; 99 100 final String[] results = new String[mLength]; 101 for (int i = mLength - 1; i >= 0; --i) { 102 results[mLength - i - 1] = mSuggestions[i]; 103 } 104 return results; 105 } 106 } 107 108 private Dictionary getDictionary(final String locale) { 109 Dictionary dictionary = mDictionaries.get(locale); 110 if (null == dictionary) { 111 final Resources resources = getResources(); 112 final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); 113 final Locale localeObject = Utils.constructLocaleFromString(locale); 114 dictionary = DictionaryFactory.createDictionaryFromManager(this, localeObject, 115 fallbackResourceId); 116 mDictionaries.put(locale, dictionary); 117 } 118 return dictionary; 119 } 120 121 private class AndroidSpellCheckerSession extends Session { 122 @Override 123 public void onCreate() { 124 } 125 126 // Note : this must be reentrant 127 /** 128 * Gets a list of suggestions for a specific string. This returns a list of possible 129 * corrections for the text passed as an arguments. It may split or group words, and 130 * even perform grammatical analysis. 131 */ 132 @Override 133 public SuggestionsInfo onGetSuggestions(final TextInfo textInfo, 134 final int suggestionsLimit) { 135 final String locale = getLocale(); 136 final Dictionary dictionary = getDictionary(locale); 137 final String text = textInfo.getText(); 138 139 final SuggestionsGatherer suggestionsGatherer = 140 new SuggestionsGatherer(suggestionsLimit); 141 final WordComposer composer = new WordComposer(); 142 final int length = text.length(); 143 for (int i = 0; i < length; ++i) { 144 int character = text.codePointAt(i); 145 composer.add(character, new int[] { character }, 146 WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); 147 } 148 dictionary.getWords(composer, suggestionsGatherer, mProximityInfo); 149 final boolean isInDict = dictionary.isValidWord(text); 150 final String[] suggestions = suggestionsGatherer.getGatheredSuggestions(); 151 152 final int flags = 153 (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0) 154 | (null != suggestions 155 ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0); 156 return new SuggestionsInfo(flags, suggestions); 157 } 158 } 159} 160