Skip to content
Snippets Groups Projects
AndroidSpellCheckerSession.java 6.79 KiB
/*
 * 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);
        }
    }
}