| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.inputmethod.latin.spellcheck; |
| |
| import android.os.Binder; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.textservice.SentenceSuggestionsInfo; |
| import android.view.textservice.SuggestionsInfo; |
| import android.view.textservice.TextInfo; |
| |
| import com.android.inputmethod.latin.CollectionUtils; |
| |
| import java.util.ArrayList; |
| |
| public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession { |
| private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName(); |
| private static final boolean DBG = false; |
| private final static String[] EMPTY_STRING_ARRAY = new String[0]; |
| |
| public AndroidSpellCheckerSession(AndroidSpellCheckerService service) { |
| super(service); |
| } |
| |
| private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti, |
| SentenceSuggestionsInfo ssi) { |
| final String typedText = ti.getText(); |
| if (!typedText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { |
| return null; |
| } |
| final int N = ssi.getSuggestionsCount(); |
| final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList(); |
| final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList(); |
| final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = |
| CollectionUtils.newArrayList(); |
| String currentWord = null; |
| for (int i = 0; i < N; ++i) { |
| final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i); |
| final int flags = si.getSuggestionsAttributes(); |
| if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) { |
| continue; |
| } |
| final int offset = ssi.getOffsetAt(i); |
| final int length = ssi.getLengthAt(i); |
| final String subText = typedText.substring(offset, offset + length); |
| final String prevWord = currentWord; |
| currentWord = subText; |
| if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { |
| continue; |
| } |
| final String[] splitTexts = |
| subText.split(AndroidSpellCheckerService.SINGLE_QUOTE, -1); |
| if (splitTexts == null || splitTexts.length <= 1) { |
| continue; |
| } |
| final int splitNum = splitTexts.length; |
| for (int j = 0; j < splitNum; ++j) { |
| final String splitText = splitTexts[j]; |
| if (TextUtils.isEmpty(splitText)) { |
| continue; |
| } |
| if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWord) == null) { |
| continue; |
| } |
| final int newLength = splitText.length(); |
| // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO |
| final int newFlags = 0; |
| final SuggestionsInfo newSi = |
| new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY); |
| newSi.setCookieAndSequence(si.getCookie(), si.getSequence()); |
| if (DBG) { |
| Log.d(TAG, "Override and remove old span over: " + splitText + ", " |
| + offset + "," + newLength); |
| } |
| additionalOffsets.add(offset); |
| additionalLengths.add(newLength); |
| additionalSuggestionsInfos.add(newSi); |
| } |
| } |
| final int additionalSize = additionalOffsets.size(); |
| if (additionalSize <= 0) { |
| return null; |
| } |
| final int suggestionsSize = N + additionalSize; |
| final int[] newOffsets = new int[suggestionsSize]; |
| final int[] newLengths = new int[suggestionsSize]; |
| final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize]; |
| int i; |
| for (i = 0; i < N; ++i) { |
| newOffsets[i] = ssi.getOffsetAt(i); |
| newLengths[i] = ssi.getLengthAt(i); |
| newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i); |
| } |
| for (; i < suggestionsSize; ++i) { |
| newOffsets[i] = additionalOffsets.get(i - N); |
| newLengths[i] = additionalLengths.get(i - N); |
| newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N); |
| } |
| return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths); |
| } |
| |
| @Override |
| public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, |
| int suggestionsLimit) { |
| final SentenceSuggestionsInfo[] retval = |
| super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit); |
| if (retval == null || retval.length != textInfos.length) { |
| return retval; |
| } |
| for (int i = 0; i < retval.length; ++i) { |
| final SentenceSuggestionsInfo tempSsi = |
| fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]); |
| if (tempSsi != null) { |
| retval[i] = tempSsi; |
| } |
| } |
| return retval; |
| } |
| |
| @Override |
| public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos, |
| int suggestionsLimit, boolean sequentialWords) { |
| long ident = Binder.clearCallingIdentity(); |
| try { |
| final int length = textInfos.length; |
| final SuggestionsInfo[] retval = new SuggestionsInfo[length]; |
| for (int i = 0; i < length; ++i) { |
| final String prevWord; |
| if (sequentialWords && i > 0) { |
| final String prevWordCandidate = textInfos[i - 1].getText(); |
| // Note that an empty string would be used to indicate the initial word |
| // in the future. |
| prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate; |
| } else { |
| prevWord = null; |
| } |
| retval[i] = onGetSuggestionsInternal(textInfos[i], prevWord, suggestionsLimit); |
| retval[i].setCookieAndSequence(textInfos[i].getCookie(), |
| textInfos[i].getSequence()); |
| } |
| return retval; |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| } |