WordComposer.java revision fae1ba767ca177510adc08b363987f67bbf40d90
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
196b1f500da451de56932a8b2a99c63857994ece85Jean Chalardimport com.android.inputmethod.keyboard.Key;
203708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
215fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalardimport com.android.inputmethod.latin.utils.StringUtils;
22887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka
23c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalardimport java.util.Arrays;
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * A place to store the currently composing word with information such as adjacent key codes as well
27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
28a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class WordComposer {
29ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataoka    private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
300e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    private static final boolean DBG = LatinImeLogger.sDBG;
318fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
32adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_OFF = 0;
33adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
34adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // aren't used anywhere in the code
35adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1;
36adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3;
37adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_AUTO_SHIFTED = 0x5;
38adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
39adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard
405fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // An array of code points representing the characters typed so far.
415fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // The array is limited to MAX_WORD_LENGTH code points, but mTypedWord extends past that
425fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // and mCodePointSize can go past that. If mCodePointSize is greater than MAX_WORD_LENGTH,
435fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // this just does not contain the associated code points past MAX_WORD_LENGTH.
4401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    private int[] mPrimaryKeyCodes;
4596b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa    private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
465fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // This is the typed word, as a StringBuilder. This has the same contents as mPrimaryKeyCodes
475fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // but under a StringBuilder representation for ease of use, depending on what is more useful
485fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // at any given time. However this is not limited in size, while mPrimaryKeyCodes is limited
495fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // to MAX_WORD_LENGTH code points.
506b4ce58fc6216b9befd0567b56522ee32f2471a2Tadashi G. Takaoka    private final StringBuilder mTypedWord;
512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // The previous word (before the composing word). Used as context for suggestions. May be null
522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // after resetting and before starting a new composing word, or when there is no context like
532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // at the start of text for example.
542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private String mPreviousWord;
55bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String mAutoCorrection;
564b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    private boolean mIsResumed;
57d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    private boolean mIsBatchMode;
58d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user
59d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // gestures a word, is displeased with the results and hits backspace, then gestures again.
60d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // At the very least we should avoid re-suggesting the same thing, and to do that we memorize
61d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // the rejected suggestion in this variable.
62d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // TODO: this should be done in a comprehensive way by the User History feature instead of
63d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // as an ad-hockery here.
64d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    private String mRejectedBatchModeSuggestion;
654a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
66be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    // Cache these values for performance
674a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    private int mCapsCount;
68e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    private int mDigitsCount;
69adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    private int mCapitalizedMode;
70117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    private int mTrailingSingleQuotesCount;
715fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // This is the number of code points entered so far. This is not limited to MAX_WORD_LENGTH.
725fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // In general, this contains the size of mPrimaryKeyCodes, except when this is greater than
735fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // MAX_WORD_LENGTH in which case mPrimaryKeyCodes only contain the first MAX_WORD_LENGTH
745fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // code points.
7501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    private int mCodePointSize;
766a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    private int mCursorPositionWithinWord;
77c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
78923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
790b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether the user chose to capitalize the first char of the word.
80923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
810b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
82923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
83979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public WordComposer() {
8496b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa        mPrimaryKeyCodes = new int[MAX_WORD_LENGTH];
8596b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa        mTypedWord = new StringBuilder(MAX_WORD_LENGTH);
86be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
87117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
884b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
89d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
906a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
91d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
922fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviousWord = null;
9301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        refreshSize();
94923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
95923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
96bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public WordComposer(final WordComposer source) {
9701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
98be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord = new StringBuilder(source.mTypedWord);
9971538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.copy(source.mInputPointers);
100ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mCapsCount = source.mCapsCount;
101e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = source.mDigitsCount;
102ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
103adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        mCapitalizedMode = source.mCapitalizedMode;
104117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
1054b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = source.mIsResumed;
106d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = source.mIsBatchMode;
1076a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = source.mCursorPositionWithinWord;
108d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion;
1092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviousWord = source.mPreviousWord;
11001ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        refreshSize();
111979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
112979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
113923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
114923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Clear out the keys registered so far.
115923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
116923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void reset() {
117be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.setLength(0);
118be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
1194a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        mCapsCount = 0;
120e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
121ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = false;
122117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
1234b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
124d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
1256a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
126d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
1272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviousWord = null;
12801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        refreshSize();
12901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    }
13001ab7c8b59a7f12862fbd95fb252e56719f1757fsatok
131e55c23e4b0b8d9d66349a3b275d0fa1540d7450aKen Wakasa    private final void refreshSize() {
13201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length());
133923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
134923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
135923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
136923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Number of keystrokes in the composing word.
137923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the number of keystrokes
138923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
139ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    public final int size() {
14001ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return mCodePointSize;
141923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
142923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
143196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    public final boolean isComposingWord() {
14401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return size() > 0;
145196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    }
146196d82cdd740580ed79d801483dbc282be85d076Jean Chalard
1479611b281e18ac71d825ff5bc771a111423772cb3satok    // TODO: make sure that the index should not exceed MAX_WORD_LENGTH
14801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    public int getCodeAt(int index) {
14996b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa        if (index >= MAX_WORD_LENGTH) {
1509611b281e18ac71d825ff5bc771a111423772cb3satok            return -1;
1519611b281e18ac71d825ff5bc771a111423772cb3satok        }
15201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return mPrimaryKeyCodes[index];
153923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
154923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1550e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    public int getCodeBeforeCursor() {
1560e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (mCursorPositionWithinWord < 1 || mCursorPositionWithinWord > mPrimaryKeyCodes.length) {
1570e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            return Constants.NOT_A_CODE;
1580e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        }
1590e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        return mPrimaryKeyCodes[mCursorPositionWithinWord - 1];
1600e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    }
1610e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard
16271538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka    public InputPointers getInputPointers() {
16371538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        return mInputPointers;
1648fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1658fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
166bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private static boolean isFirstCharCapitalized(final int index, final int codePoint,
167bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final boolean previous) {
168ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (index == 0) return Character.isUpperCase(codePoint);
169436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka        return previous && !Character.isUpperCase(codePoint);
170ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    }
171ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka
172923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
173c61cd79229b1871d0f603a23389695d7f7751e66Tadashi G. Takaoka     * Add a new keystroke, with the pressed key's code point with the touch point coordinates.
174923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
175bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void add(final int primaryCode, final int keyX, final int keyY) {
17601ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        final int newIndex = size();
1779159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard        mTypedWord.appendCodePoint(primaryCode);
17801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        refreshSize();
1796a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
18096b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa        if (newIndex < MAX_WORD_LENGTH) {
181240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE
18267094f5bdece00994f70c6f1fa9a6ff7b8f3c3c1satok                    ? Character.toLowerCase(primaryCode) : primaryCode;
183eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // In the batch input mode, the {@code mInputPointers} holds batch input points and
184eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // shouldn't be overridden by the "typed key" coordinates
185eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // (See {@link #setBatchInputWord}).
186eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            if (!mIsBatchMode) {
187eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                // TODO: Set correct pointer id and time
188eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0);
189eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
1908fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
191ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = isFirstCharCapitalized(
192ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka                newIndex, primaryCode, mIsFirstCharCapitalized);
193ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (Character.isUpperCase(primaryCode)) mCapsCount++;
194e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        if (Character.isDigit(primaryCode)) mDigitsCount++;
195240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_SINGLE_QUOTE == primaryCode) {
196117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            ++mTrailingSingleQuotesCount;
197117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        } else {
198117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            mTrailingSingleQuotesCount = 0;
199117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        }
200be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
201923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
202923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2036a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    public void setCursorPositionWithinWord(final int posWithinWord) {
2046a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = posWithinWord;
2056a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2066a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
2070e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    public boolean isCursorFrontOrMiddleOfComposingWord() {
2080e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (DBG && mCursorPositionWithinWord > mCodePointSize) {
2090e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord
2100e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    + "in a word of size " + mCodePointSize);
2110e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        }
2120e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        return mCursorPositionWithinWord != mCodePointSize;
2136a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2146a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
215f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    /**
216f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * When the cursor is moved by the user, we need to update its position.
217f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * If it falls inside the currently composing word, we don't reset the composition, and
218f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * only update the cursor position.
219f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     *
220f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @param expectedMoveAmount How many java chars to move the cursor. Negative values move
221f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * the cursor backward, positive values move the cursor forward.
222f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @return true if the cursor is still inside the composing word, false otherwise.
223f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     */
224f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) {
225f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        int actualMoveAmountWithinWord = 0;
226f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        int cursorPos = mCursorPositionWithinWord;
2275fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard        final int[] codePoints;
2285fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard        if (mCodePointSize >= MAX_WORD_LENGTH) {
2295fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard            // If we have more than MAX_WORD_LENGTH characters, we don't have everything inside
2305fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard            // mPrimaryKeyCodes. This should be rare enough that we can afford to just compute
2315fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard            // the array on the fly when this happens.
2325fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard            codePoints = StringUtils.toCodePointArray(mTypedWord.toString());
2335fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard        } else {
2345fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard            codePoints = mPrimaryKeyCodes;
2355fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard        }
236f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        if (expectedMoveAmount >= 0) {
237f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor forward for the expected amount or until the end of the word has
238f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // been reached, whichever comes first.
239f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            while (actualMoveAmountWithinWord < expectedMoveAmount && cursorPos < mCodePointSize) {
2405fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard                actualMoveAmountWithinWord += Character.charCount(codePoints[cursorPos]);
241f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                ++cursorPos;
242f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
243f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        } else {
244f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor backward for the expected amount or until the start of the word
245f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // has been reached, whichever comes first.
246f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            while (actualMoveAmountWithinWord > expectedMoveAmount && cursorPos > 0) {
247f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                --cursorPos;
2485fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard                actualMoveAmountWithinWord -= Character.charCount(codePoints[cursorPos]);
249f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
250f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        }
251f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // If the actual and expected amounts differ, we crossed the start or the end of the word
252f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // so the result would not be inside the composing word.
253f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        if (actualMoveAmountWithinWord != expectedMoveAmount) return false;
254f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        mCursorPositionWithinWord = cursorPos;
255f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        return true;
256f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    }
257f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard
258bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputPointers(final InputPointers batchPointers) {
259eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mInputPointers.set(batchPointers);
260d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = true;
261d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
262d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
263bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputWord(final String word) {
264eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        reset();
265eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mIsBatchMode = true;
266eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final int length = word.length();
267eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
268eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int codePoint = Character.codePointAt(word, i);
269eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // We don't want to override the batch input points that are held in mInputPointers
270eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // (See {@link #add(int,int,int)}).
271ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka            add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
272eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
273eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    }
274eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
275923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
276244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka     * Add a dummy key by retrieving reasonable coordinates
2776b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
2785701a6647f43eaa1eb4cb84c9c063d8440fe24ceTadashi G. Takaoka    public void addKeyInfo(final int codePoint, final Keyboard keyboard) {
279d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        final int x, y;
280d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        final Key key;
281d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        if (keyboard != null && (key = keyboard.getKey(codePoint)) != null) {
2827dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            x = key.getX() + key.getWidth() / 2;
2837dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            y = key.getY() + key.getHeight() / 2;
284d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        } else {
285d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka            x = Constants.NOT_A_COORDINATE;
286d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka            y = Constants.NOT_A_COORDINATE;
2876b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
288d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        add(codePoint, x, y);
2896b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
2906b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
2916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
2926b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Set the currently composing word to the one passed as an argument.
2936b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
2942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * @param word the char sequence to set as the composing word.
2952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * @param previousWord the previous word, to use as context for suggestions. Can be null if
2962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     *   the context is nil (typically, at start of text).
2972fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * @param keyboard the keyboard this is typed on, for coordinate info/proximity.
2986b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
2992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void setComposingWord(final CharSequence word, final String previousWord,
3002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final Keyboard keyboard) {
3016b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        reset();
3026b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final int length = word.length();
3039159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard        for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
304d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka            final int codePoint = Character.codePointAt(word, i);
305a492790982c6d7df62f66344db30b31995800e1bJean Chalard            addKeyInfo(codePoint, keyboard);
3066b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
3074b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
3082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviousWord = previousWord;
3096b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
3106b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
3116b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
312923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Delete the last keystroke as a result of hitting backspace.
313923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
314923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void deleteLast() {
31501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        final int size = size();
316ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (size > 0) {
3179159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            // Note: mTypedWord.length() and mCodes.length differ when there are surrogate pairs
3189159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            final int stringBuilderLength = mTypedWord.length();
3199159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            if (stringBuilderLength < size) {
3209159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard                throw new RuntimeException(
3219159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard                        "In WordComposer: mCodes and mTypedWords have non-matching lengths");
3229159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            }
3239159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            final int lastChar = mTypedWord.codePointBefore(stringBuilderLength);
3249159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            if (Character.isSupplementaryCodePoint(lastChar)) {
3259159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard                mTypedWord.delete(stringBuilderLength - 2, stringBuilderLength);
3269159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            } else {
3279159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard                mTypedWord.deleteCharAt(stringBuilderLength - 1);
3289159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard            }
329ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            if (Character.isUpperCase(lastChar)) mCapsCount--;
330e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard            if (Character.isDigit(lastChar)) mDigitsCount--;
33101ab7c8b59a7f12862fbd95fb252e56719f1757fsatok            refreshSize();
332d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka        }
3339159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard        // We may have deleted the last one.
33401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        if (0 == size()) {
335ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            mIsFirstCharCapitalized = false;
336117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        }
337117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        if (mTrailingSingleQuotesCount > 0) {
338117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            --mTrailingSingleQuotesCount;
339c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard        } else {
340825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard            int i = mTypedWord.length();
341825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard            while (i > 0) {
342825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard                i = mTypedWord.offsetByCodePoints(i, -1);
343240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                if (Constants.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break;
344117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard                ++mTrailingSingleQuotesCount;
345117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            }
3468fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
3476a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
348be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
349923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
350923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
351923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
352923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the word as it was typed, without any correction applied.
353117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * @return the word that was typed so far. Never returns null.
354923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
3555c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard    public String getTypedWord() {
356be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mTypedWord.toString();
357923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
358923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
3592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public String getPreviousWord() {
3602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return mPreviousWord;
3612fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
363923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
364923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Whether or not the user typed a capital letter as the first letter in the word
365923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return capitalization preference
366923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
3670b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isFirstCharCapitalized() {
3680b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return mIsFirstCharCapitalized;
369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
3700b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
371117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    public int trailingSingleQuotesCount() {
372117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        return mTrailingSingleQuotesCount;
373c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard    }
374c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
3750b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    /**
3760b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether or not all of the user typed chars are upper case
3770b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * @return true if all user typed chars are upper case, false otherwise
3780b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     */
3790b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isAllUpperCase() {
380ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        if (size() <= 1) {
381ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard            return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
382ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard                    || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED;
383ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        } else {
384ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard            return mCapsCount == size();
385ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        }
3861eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    }
3871eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard
3881eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    public boolean wasShiftedNoLock() {
3891eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED
3901eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard                || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED;
3910b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    }
3920b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
393923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
3944a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     * Returns true if more than one character is upper case, otherwise returns false.
3954a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     */
3964a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    public boolean isMostlyCaps() {
3974a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        return mCapsCount > 1;
3984a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    }
3991c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
4000fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard    /**
401e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     * Returns true if we have digits in the composing word.
402e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     */
403e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    public boolean hasDigits() {
404e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        return mDigitsCount > 0;
405e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    }
406e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard
407e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    /**
4082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * Saves the caps mode and the previous word at the start of composing.
409adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     *
4102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * WordComposer needs to know about the caps mode for several reasons. The first is, we need
4112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * to know after the fact what the reason was, to register the correct form into the user
4122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * history dictionary: if the word was automatically capitalized, we should insert it in
4132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * all-lower case but if it's a manual pressing of shift, then it should be inserted as is.
414adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * Also, batch input needs to know about the current caps mode to display correctly
415adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * capitalized suggestions.
416adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * @param mode the mode at the time of start
4172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * @param previousWord the previous word as context for suggestions. May be null if none.
4181c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
4192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
4202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final String previousWord) {
421adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        mCapitalizedMode = mode;
4222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviousWord = previousWord;
4231c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
4241c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
4251c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
4261c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Returns whether the word was automatically capitalized.
4271c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @return whether the word was automatically capitalized
4281c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
429adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public boolean wasAutoCapitalized() {
430adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
431adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard                || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
4321c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
433117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
434117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
435117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * Sets the auto-correction for this word.
436117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
437bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setAutoCorrection(final String correction) {
438be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = correction;
439117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
440117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
441117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
442f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard     * @return the auto-correction for this word, or null if none.
443117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
444bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public String getAutoCorrectionOrNull() {
445be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mAutoCorrection;
446117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
447c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
4484b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    /**
4494b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     * @return whether we started composing this word by resuming suggestion on an existing string
4504b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     */
4514b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    public boolean isResumed() {
4524b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        return mIsResumed;
4534b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    }
4544b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard
455267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard    // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
45666bb563535dbe3672f99f75bd71763a551444867Jean Chalard    public LastComposedWord commitWord(final int type, final String committedWord,
457bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final String separatorString, final String prevWord) {
4589271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
4599271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
4609271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // the last composed word to ensure this does not happen.
46101ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        final int[] primaryKeyCodes = mPrimaryKeyCodes;
46296b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa        mPrimaryKeyCodes = new int[MAX_WORD_LENGTH];
46301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes,
464a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                mInputPointers, mTypedWord.toString(), committedWord, separatorString,
465b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard                prevWord, mCapitalizedMode);
46671538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.reset();
4679271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
4689271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard                && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
469449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard            lastComposedWord.deactivate();
470449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard        }
471e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mCapsCount = 0;
472e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
473e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mIsBatchMode = false;
474fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        final boolean isWhitespace = 1 == StringUtils.codePointCount(separatorString)
475fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard                && Character.isWhitespace(separatorString.codePointAt(0));
476fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        // If not whitespace, we don't use the previous word for suggestion. This is consistent
477fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        // with how we get the previous word for suggestion: see RichInputConnection#spaceRegex and
478fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        // LatinIME#getNthPreviousWordForSuggestion.
479fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        mPreviousWord = isWhitespace ? mTypedWord.toString() : null;
480be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.setLength(0);
4818914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCodePointSize = 0;
4822a37fb9d30848aee42757546e8478cb7a9e45bc6Jean Chalard        mTrailingSingleQuotesCount = 0;
483e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mIsFirstCharCapitalized = false;
4848914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCapitalizedMode = CAPS_MODE_OFF;
48501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        refreshSize();
486be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
4876a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
4884b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
489d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
4901f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard        return lastComposedWord;
491c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
492fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard
493fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard    public void doubleSpacePeriod() {
494fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        // When a period was entered with a double space, the separator we got has been
495fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        // changed by a period (see #commitWord). We should not use the previous word for
496fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        // suggestion.
497fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard        mPreviousWord = null;
498fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard    }
499c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
5002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
5012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final String previousWord) {
50201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
50371538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.set(lastComposedWord.mInputPointers);
504be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.setLength(0);
505be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.append(lastComposedWord.mTypedWord);
50601ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        refreshSize();
507b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard        mCapitalizedMode = lastComposedWord.mCapitalizedMode;
508cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
5096a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
510d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
5114b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
5122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviousWord = previousWord;
5139e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard    }
514d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
515d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    public boolean isBatchMode() {
516d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        return mIsBatchMode;
517d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
518d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
519d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public void setRejectedBatchModeSuggestion(final String rejectedSuggestion) {
520d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = rejectedSuggestion;
521d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
522d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
523d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public String getRejectedBatchModeSuggestion() {
524d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        return mRejectedBatchModeSuggestion;
525d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
526923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
527