WordComposer.java revision 0f913ff5ba71c40a4492994a23010336cd25be8e
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
30fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
70fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
90fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard *
10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
197ede642df417c0f732573f639970b138f0bee18cJean Chalardimport com.android.inputmethod.event.CombinerChain;
20f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport com.android.inputmethod.event.Event;
21f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport com.android.inputmethod.latin.utils.CollectionUtils;
22d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalardimport com.android.inputmethod.latin.utils.CoordinateUtils;
235fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalardimport com.android.inputmethod.latin.utils.StringUtils;
24887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka
25f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport java.util.ArrayList;
26f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport java.util.Collections;
27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * A place to store the currently composing word with information such as adjacent key codes as well
30923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
31a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class WordComposer {
32ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataoka    private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
330e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    private static final boolean DBG = LatinImeLogger.sDBG;
348fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
35adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_OFF = 0;
36adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
37adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // aren't used anywhere in the code
38adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1;
39adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3;
40adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_AUTO_SHIFTED = 0x5;
41adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
42adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard
437ede642df417c0f732573f639970b138f0bee18cJean Chalard    private CombinerChain mCombinerChain;
447ede642df417c0f732573f639970b138f0bee18cJean Chalard
4575715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    // An array of code points representing the characters typed so far.
4675715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    // The array is limited to MAX_WORD_LENGTH code points, but mTypedWord extends past that
4775715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    // and mCodePointSize can go past that. If mCodePointSize is greater than MAX_WORD_LENGTH,
4875715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    // this just does not contain the associated code points past MAX_WORD_LENGTH.
4975715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    private int[] mPrimaryKeyCodes;
50f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard    // The list of events that served to compose this string.
51f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard    private final ArrayList<Event> mEvents;
5296b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa    private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // The previous word (before the composing word). Used as context for suggestions. May be null
542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // after resetting and before starting a new composing word, or when there is no context like
557cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // at the start of text for example. It can also be set to null externally when the user
567cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // enters a separator that does not let bigrams across, like a period or a comma.
577cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    private String mPreviousWordForSuggestion;
58bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String mAutoCorrection;
594b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    private boolean mIsResumed;
60d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    private boolean mIsBatchMode;
61d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user
62d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // gestures a word, is displeased with the results and hits backspace, then gestures again.
63d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // At the very least we should avoid re-suggesting the same thing, and to do that we memorize
64d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // the rejected suggestion in this variable.
65d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // TODO: this should be done in a comprehensive way by the User History feature instead of
66d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // as an ad-hockery here.
67d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    private String mRejectedBatchModeSuggestion;
684a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
69be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    // Cache these values for performance
703285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard    private CharSequence mTypedWordCache;
714a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    private int mCapsCount;
72e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    private int mDigitsCount;
73adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    private int mCapitalizedMode;
74117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    private int mTrailingSingleQuotesCount;
755fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // This is the number of code points entered so far. This is not limited to MAX_WORD_LENGTH.
765fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // In general, this contains the size of mPrimaryKeyCodes, except when this is greater than
775fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // MAX_WORD_LENGTH in which case mPrimaryKeyCodes only contain the first MAX_WORD_LENGTH
785fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // code points.
7901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    private int mCodePointSize;
806a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    private int mCursorPositionWithinWord;
81c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
82923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
830b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether the user chose to capitalize the first char of the word.
84923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
850b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
86923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
87979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public WordComposer() {
887ede642df417c0f732573f639970b138f0bee18cJean Chalard        mCombinerChain = new CombinerChain();
8975715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        mPrimaryKeyCodes = new int[MAX_WORD_LENGTH];
90f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents = CollectionUtils.newArrayList();
91be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
92117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
934b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
94d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
956a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
96d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
977cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard        mPreviousWordForSuggestion = null;
983285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
99923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
100923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
101923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Clear out the keys registered so far.
103923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
104923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void reset() {
105cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
106f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
107be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
1084a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        mCapsCount = 0;
109e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
110ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = false;
111117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
1124b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
113d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
1146a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
115d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
1167cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard        mPreviousWordForSuggestion = null;
1173285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
11801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    }
11901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok
1203285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard    private final void refreshTypedWordCache() {
1213285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        mTypedWordCache = mCombinerChain.getComposingWordWithCombiningFeedback();
1223285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        mCodePointSize = Character.codePointCount(mTypedWordCache, 0, mTypedWordCache.length());
123923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
124923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
125923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
126923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Number of keystrokes in the composing word.
127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the number of keystrokes
128923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
12970d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard    // This may be made public if need be, but right now it's not used anywhere
13070d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard    /* package for tests */ int size() {
13101ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return mCodePointSize;
132923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
133923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
13470d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard    public boolean isSingleLetter() {
13570d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard        return size() == 1;
13670d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard    }
13770d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard
1388e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard    // When the composition contains trailing quotes, we don't pass them to the suggestion engine.
1398e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard    // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider
1408e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard    // single quotes as separators because of their very common use as apostrophes.
1418e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard    public int sizeWithoutTrailingSingleQuotes() {
1428e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard        return size() - mTrailingSingleQuotesCount;
1438e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard    }
1448e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard
145196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    public final boolean isComposingWord() {
14601ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return size() > 0;
147196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    }
148196d82cdd740580ed79d801483dbc282be85d076Jean Chalard
14975715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    // TODO: make sure that the index should not exceed MAX_WORD_LENGTH
15075715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    public int getCodeAt(int index) {
15175715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        if (index >= MAX_WORD_LENGTH) {
15275715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard            return -1;
15375715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        }
15475715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        return mPrimaryKeyCodes[index];
15575715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard    }
15675715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard
15771538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka    public InputPointers getInputPointers() {
15871538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        return mInputPointers;
1598fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1608fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
161bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private static boolean isFirstCharCapitalized(final int index, final int codePoint,
162bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final boolean previous) {
163ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (index == 0) return Character.isUpperCase(codePoint);
164436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka        return previous && !Character.isUpperCase(codePoint);
165ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    }
166ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka
167923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
168f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard     * Add a new event for a key stroke, with the pressed key's code point with the touch point
169f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard     * coordinates.
170923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
171f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard    public void add(final Event event) {
172f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        final int primaryCode = event.mCodePoint;
173f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        final int keyX = event.mX;
174f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        final int keyY = event.mY;
17501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        final int newIndex = size();
1760f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        processEvent(event);
1776a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
17896b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa        if (newIndex < MAX_WORD_LENGTH) {
17975715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard            mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE
18075715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard                    ? Character.toLowerCase(primaryCode) : primaryCode;
181eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // In the batch input mode, the {@code mInputPointers} holds batch input points and
182eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // shouldn't be overridden by the "typed key" coordinates
183eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // (See {@link #setBatchInputWord}).
184eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            if (!mIsBatchMode) {
185eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                // TODO: Set correct pointer id and time
186ad78058a93492d4f114c6a6eb56177be9231a9ebTadashi G. Takaoka                mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
187eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
1888fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
189ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = isFirstCharCapitalized(
190ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka                newIndex, primaryCode, mIsFirstCharCapitalized);
191ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (Character.isUpperCase(primaryCode)) mCapsCount++;
192e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        if (Character.isDigit(primaryCode)) mDigitsCount++;
193240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_SINGLE_QUOTE == primaryCode) {
194117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            ++mTrailingSingleQuotesCount;
195117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        } else {
196117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            mTrailingSingleQuotesCount = 0;
197117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        }
198be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
199923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
200923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2010f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard    private void processEvent(final Event event) {
2020f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        mCombinerChain.processEvent(mEvents, event);
2030f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        mEvents.add(event);
2040f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        refreshTypedWordCache();
2050f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard    }
2060f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard
2078bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard    /**
2088bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard     * Delete the last composing unit as a result of hitting backspace.
2098bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard     */
2108bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard    public void deleteLast(final Event event) {
2110f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        processEvent(event);
2128bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        // We may have deleted the last one.
2138bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        if (0 == size()) {
2148bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard            mIsFirstCharCapitalized = false;
2158bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        }
2168bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        if (mTrailingSingleQuotesCount > 0) {
2178bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard            --mTrailingSingleQuotesCount;
2188bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        } else {
2198bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard            int i = mTypedWordCache.length();
2208bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard            while (i > 0) {
2218bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard                i = Character.offsetByCodePoints(mTypedWordCache, i, -1);
2228bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard                if (Constants.CODE_SINGLE_QUOTE != Character.codePointAt(mTypedWordCache, i)) {
2238bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard                    break;
2248bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard                }
2258bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard                ++mTrailingSingleQuotesCount;
2268bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard            }
2278bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        }
2288bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        mCursorPositionWithinWord = mCodePointSize;
2298bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard        mAutoCorrection = null;
2308bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard    }
2318bb2e99aefd6d98032f00d7d7fa917220d99188aJean Chalard
2326a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    public void setCursorPositionWithinWord(final int posWithinWord) {
2336a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = posWithinWord;
234f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        // TODO: compute where that puts us inside the events
2356a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2366a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
2370e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    public boolean isCursorFrontOrMiddleOfComposingWord() {
2380e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (DBG && mCursorPositionWithinWord > mCodePointSize) {
2390e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord
2400e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    + "in a word of size " + mCodePointSize);
2410e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        }
2420e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        return mCursorPositionWithinWord != mCodePointSize;
2436a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2446a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
245f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    /**
246f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * When the cursor is moved by the user, we need to update its position.
247f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * If it falls inside the currently composing word, we don't reset the composition, and
248f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * only update the cursor position.
249f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     *
250f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @param expectedMoveAmount How many java chars to move the cursor. Negative values move
251f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * the cursor backward, positive values move the cursor forward.
252f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @return true if the cursor is still inside the composing word, false otherwise.
253f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     */
254f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) {
255cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        // TODO: should uncommit the composing feedback
256cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
257f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        int actualMoveAmountWithinWord = 0;
258f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        int cursorPos = mCursorPositionWithinWord;
25975715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        final int[] codePoints;
26075715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        if (mCodePointSize >= MAX_WORD_LENGTH) {
26175715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard            // If we have more than MAX_WORD_LENGTH characters, we don't have everything inside
26275715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard            // mPrimaryKeyCodes. This should be rare enough that we can afford to just compute
26375715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard            // the array on the fly when this happens.
2643285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard            codePoints = StringUtils.toCodePointArray(mTypedWordCache);
26575715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        } else {
26675715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard            codePoints = mPrimaryKeyCodes;
26775715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        }
268f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        if (expectedMoveAmount >= 0) {
269f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor forward for the expected amount or until the end of the word has
270f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // been reached, whichever comes first.
271f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            while (actualMoveAmountWithinWord < expectedMoveAmount && cursorPos < mCodePointSize) {
2725fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard                actualMoveAmountWithinWord += Character.charCount(codePoints[cursorPos]);
273f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                ++cursorPos;
274f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
275f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        } else {
276f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor backward for the expected amount or until the start of the word
277f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // has been reached, whichever comes first.
278f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            while (actualMoveAmountWithinWord > expectedMoveAmount && cursorPos > 0) {
279f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                --cursorPos;
2805fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard                actualMoveAmountWithinWord -= Character.charCount(codePoints[cursorPos]);
281f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
282f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        }
283f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // If the actual and expected amounts differ, we crossed the start or the end of the word
284f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // so the result would not be inside the composing word.
285f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        if (actualMoveAmountWithinWord != expectedMoveAmount) return false;
286f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        mCursorPositionWithinWord = cursorPos;
287f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        return true;
288f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    }
289f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard
290bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputPointers(final InputPointers batchPointers) {
291eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mInputPointers.set(batchPointers);
292d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = true;
293d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
294d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
295bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputWord(final String word) {
296eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        reset();
297eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mIsBatchMode = true;
298eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final int length = word.length();
299eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
300eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int codePoint = Character.codePointAt(word, i);
301eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // We don't want to override the batch input points that are held in mInputPointers
302eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // (See {@link #add(int,int,int)}).
303f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard            add(Event.createEventForCodePointFromUnknownSource(codePoint));
304eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
305eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    }
306eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
307923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
3086b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Set the currently composing word to the one passed as an argument.
3096b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
310d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard     * @param codePoints the code points to set as the composing word.
311d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard     * @param coordinates the x, y coordinates of the key in the CoordinateUtils format
3122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * @param previousWord the previous word, to use as context for suggestions. Can be null if
3132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     *   the context is nil (typically, at start of text).
3146b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
315d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard    public void setComposingWord(final int[] codePoints, final int[] coordinates,
3161284e556f4ec998c6f3ea80905d690cbb4c9dc82Jean Chalard            final CharSequence previousWord) {
3176b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        reset();
318d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard        final int length = codePoints.length;
319d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard        for (int i = 0; i < length; ++i) {
320f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard            add(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
321f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard                    CoordinateUtils.xFromArray(coordinates, i),
322f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard                    CoordinateUtils.yFromArray(coordinates, i)));
3236b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
3244b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
3254752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard        mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString();
3266b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
3276b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
3286b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
329923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the word as it was typed, without any correction applied.
330117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * @return the word that was typed so far. Never returns null.
331923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
3325c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard    public String getTypedWord() {
3333285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        return mTypedWordCache.toString();
334923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
335923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
3367cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    public String getPreviousWordForSuggestion() {
3377cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard        return mPreviousWordForSuggestion;
3382fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
340923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
341923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Whether or not the user typed a capital letter as the first letter in the word
342923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return capitalization preference
343923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
3440b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isFirstCharCapitalized() {
3450b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return mIsFirstCharCapitalized;
346923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
3470b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
348117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    public int trailingSingleQuotesCount() {
349117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        return mTrailingSingleQuotesCount;
350c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard    }
351c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
3520b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    /**
3530b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether or not all of the user typed chars are upper case
3540b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * @return true if all user typed chars are upper case, false otherwise
3550b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     */
3560b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isAllUpperCase() {
357ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        if (size() <= 1) {
358ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard            return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
359ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard                    || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED;
360ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        } else {
361ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard            return mCapsCount == size();
362ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        }
3631eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    }
3641eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard
3651eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    public boolean wasShiftedNoLock() {
3661eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED
3671eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard                || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED;
3680b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    }
3690b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
370923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
3714a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     * Returns true if more than one character is upper case, otherwise returns false.
3724a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     */
3734a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    public boolean isMostlyCaps() {
3744a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        return mCapsCount > 1;
3754a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    }
3761c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
3770fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard    /**
378e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     * Returns true if we have digits in the composing word.
379e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     */
380e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    public boolean hasDigits() {
381e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        return mDigitsCount > 0;
382e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    }
383e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard
384e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    /**
3852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * Saves the caps mode and the previous word at the start of composing.
386adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     *
3872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * WordComposer needs to know about the caps mode for several reasons. The first is, we need
3882fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * to know after the fact what the reason was, to register the correct form into the user
3892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * history dictionary: if the word was automatically capitalized, we should insert it in
3902fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * all-lower case but if it's a manual pressing of shift, then it should be inserted as is.
391adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * Also, batch input needs to know about the current caps mode to display correctly
392adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * capitalized suggestions.
393adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * @param mode the mode at the time of start
3942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * @param previousWord the previous word as context for suggestions. May be null if none.
3951c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
3962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
3974752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard            final CharSequence previousWord) {
398adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        mCapitalizedMode = mode;
3994752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard        mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString();
4001c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
4011c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
4021c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
4031c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Returns whether the word was automatically capitalized.
4041c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @return whether the word was automatically capitalized
4051c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
406adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public boolean wasAutoCapitalized() {
407adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
408adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard                || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
4091c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
410117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
411117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
412117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * Sets the auto-correction for this word.
413117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
414bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setAutoCorrection(final String correction) {
415be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = correction;
416117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
417117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
418117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
419f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard     * @return the auto-correction for this word, or null if none.
420117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
421bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public String getAutoCorrectionOrNull() {
422be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mAutoCorrection;
423117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
424c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
4254b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    /**
4264b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     * @return whether we started composing this word by resuming suggestion on an existing string
4274b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     */
4284b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    public boolean isResumed() {
4294b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        return mIsResumed;
4304b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    }
4314b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard
432267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard    // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
4334752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard    // committedWord should contain suggestion spans if applicable.
4344752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard    public LastComposedWord commitWord(final int type, final CharSequence committedWord,
435bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final String separatorString, final String prevWord) {
4369271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
4379271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
4389271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // the last composed word to ensure this does not happen.
43975715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        final int[] primaryKeyCodes = mPrimaryKeyCodes;
44075715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        mPrimaryKeyCodes = new int[MAX_WORD_LENGTH];
44175715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes, mEvents,
4423285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard                mInputPointers, mTypedWordCache.toString(), committedWord, separatorString,
443b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard                prevWord, mCapitalizedMode);
44471538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.reset();
4459271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
4469271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard                && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
447449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard            lastComposedWord.deactivate();
448449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard        }
449e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mCapsCount = 0;
450e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
451e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mIsBatchMode = false;
4524752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard        mPreviousWordForSuggestion = committedWord.toString();
453cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
454f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
4558914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCodePointSize = 0;
4562a37fb9d30848aee42757546e8478cb7a9e45bc6Jean Chalard        mTrailingSingleQuotesCount = 0;
457e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mIsFirstCharCapitalized = false;
4588914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCapitalizedMode = CAPS_MODE_OFF;
4593285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
460be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
4616a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
4624b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
463d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
4641f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard        return lastComposedWord;
465c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
466fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard
4677cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // Call this when the recorded previous word should be discarded. This is typically called
4687cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // when the user inputs a separator that's not whitespace (including the case of the
4697cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // double-space-to-period feature).
4707cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    public void discardPreviousWordForSuggestion() {
4717cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard        mPreviousWordForSuggestion = null;
472fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard    }
473c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
4742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
4752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final String previousWord) {
47675715f7d9fb0b19c2ddaf73bf62148c7d19f0a99Jean Chalard        mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
477f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
478f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        Collections.copy(mEvents, lastComposedWord.mEvents);
47971538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.set(lastComposedWord.mInputPointers);
480cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
4813285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
482b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard        mCapitalizedMode = lastComposedWord.mCapitalizedMode;
483cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
4846a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
485d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
4864b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
4877cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard        mPreviousWordForSuggestion = previousWord;
4889e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard    }
489d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
490d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    public boolean isBatchMode() {
491d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        return mIsBatchMode;
492d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
493d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
494d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public void setRejectedBatchModeSuggestion(final String rejectedSuggestion) {
495d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = rejectedSuggestion;
496d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
497d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
498d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public String getRejectedBatchModeSuggestion() {
499d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        return mRejectedBatchModeSuggestion;
500d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
501923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
502