AndroidSpellCheckerSession.java revision e28eba5074664d5716b8e58b8d0a235746b261eb
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.inputmethod.latin.spellcheck;
18
19import android.os.Binder;
20import android.text.TextUtils;
21import android.util.Log;
22import android.view.textservice.SentenceSuggestionsInfo;
23import android.view.textservice.SuggestionsInfo;
24import android.view.textservice.TextInfo;
25
26import com.android.inputmethod.latin.utils.CollectionUtils;
27
28import java.util.ArrayList;
29
30public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
31    private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
32    private static final boolean DBG = false;
33    private final static String[] EMPTY_STRING_ARRAY = new String[0];
34
35    public AndroidSpellCheckerSession(AndroidSpellCheckerService service) {
36        super(service);
37    }
38
39    private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti,
40            SentenceSuggestionsInfo ssi) {
41        final String typedText = ti.getText();
42        if (!typedText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
43            return null;
44        }
45        final int N = ssi.getSuggestionsCount();
46        final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList();
47        final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList();
48        final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
49                CollectionUtils.newArrayList();
50        String currentWord = null;
51        for (int i = 0; i < N; ++i) {
52            final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
53            final int flags = si.getSuggestionsAttributes();
54            if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) {
55                continue;
56            }
57            final int offset = ssi.getOffsetAt(i);
58            final int length = ssi.getLengthAt(i);
59            final String subText = typedText.substring(offset, offset + length);
60            final String prevWord = currentWord;
61            currentWord = subText;
62            if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
63                continue;
64            }
65            final String[] splitTexts =
66                    subText.split(AndroidSpellCheckerService.SINGLE_QUOTE, -1);
67            if (splitTexts == null || splitTexts.length <= 1) {
68                continue;
69            }
70            final int splitNum = splitTexts.length;
71            for (int j = 0; j < splitNum; ++j) {
72                final String splitText = splitTexts[j];
73                if (TextUtils.isEmpty(splitText)) {
74                    continue;
75                }
76                if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWord) == null) {
77                    continue;
78                }
79                final int newLength = splitText.length();
80                // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO
81                final int newFlags = 0;
82                final SuggestionsInfo newSi =
83                        new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY);
84                newSi.setCookieAndSequence(si.getCookie(), si.getSequence());
85                if (DBG) {
86                    Log.d(TAG, "Override and remove old span over: " + splitText + ", "
87                            + offset + "," + newLength);
88                }
89                additionalOffsets.add(offset);
90                additionalLengths.add(newLength);
91                additionalSuggestionsInfos.add(newSi);
92            }
93        }
94        final int additionalSize = additionalOffsets.size();
95        if (additionalSize <= 0) {
96            return null;
97        }
98        final int suggestionsSize = N + additionalSize;
99        final int[] newOffsets = new int[suggestionsSize];
100        final int[] newLengths = new int[suggestionsSize];
101        final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize];
102        int i;
103        for (i = 0; i < N; ++i) {
104            newOffsets[i] = ssi.getOffsetAt(i);
105            newLengths[i] = ssi.getLengthAt(i);
106            newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i);
107        }
108        for (; i < suggestionsSize; ++i) {
109            newOffsets[i] = additionalOffsets.get(i - N);
110            newLengths[i] = additionalLengths.get(i - N);
111            newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N);
112        }
113        return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths);
114    }
115
116    @Override
117    public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
118            int suggestionsLimit) {
119        final SentenceSuggestionsInfo[] retval =
120                super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit);
121        if (retval == null || retval.length != textInfos.length) {
122            return retval;
123        }
124        for (int i = 0; i < retval.length; ++i) {
125            final SentenceSuggestionsInfo tempSsi =
126                    fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]);
127            if (tempSsi != null) {
128                retval[i] = tempSsi;
129            }
130        }
131        return retval;
132    }
133
134    @Override
135    public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
136            int suggestionsLimit, boolean sequentialWords) {
137        long ident = Binder.clearCallingIdentity();
138        try {
139            final int length = textInfos.length;
140            final SuggestionsInfo[] retval = new SuggestionsInfo[length];
141            for (int i = 0; i < length; ++i) {
142                final String prevWord;
143                if (sequentialWords && i > 0) {
144                final String prevWordCandidate = textInfos[i - 1].getText();
145                // Note that an empty string would be used to indicate the initial word
146                // in the future.
147                prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
148                } else {
149                    prevWord = null;
150                }
151                retval[i] = onGetSuggestionsInternal(textInfos[i], prevWord, suggestionsLimit);
152                retval[i].setCookieAndSequence(textInfos[i].getCookie(),
153                        textInfos[i].getSequence());
154            }
155            return retval;
156        } finally {
157            Binder.restoreCallingIdentity(ident);
158        }
159    }
160}
161