WordComposer.java revision e507d92aa3ee4ae43124c5452f20aa8ed0ecef4c
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;
44be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard    private String mCombiningSpec; // Memory so that we don't uselessly recreate the combiner chain
457ede642df417c0f732573f639970b138f0bee18cJean Chalard
46f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard    // The list of events that served to compose this string.
47f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard    private final ArrayList<Event> mEvents;
4896b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa    private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
4983c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi    // The information of previous words (before the composing word). Must not be null. Used as
5083c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi    // context for suggestions.
5183c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi    private PrevWordsInfo mPrevWordsInfo;
52bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String mAutoCorrection;
534b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    private boolean mIsResumed;
54d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    private boolean mIsBatchMode;
55d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user
56d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // gestures a word, is displeased with the results and hits backspace, then gestures again.
57d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // At the very least we should avoid re-suggesting the same thing, and to do that we memorize
58d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // the rejected suggestion in this variable.
59d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // TODO: this should be done in a comprehensive way by the User History feature instead of
60d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // as an ad-hockery here.
61d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    private String mRejectedBatchModeSuggestion;
624a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
63be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    // Cache these values for performance
643285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard    private CharSequence mTypedWordCache;
654a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    private int mCapsCount;
66e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    private int mDigitsCount;
67adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    private int mCapitalizedMode;
685fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // This is the number of code points entered so far. This is not limited to MAX_WORD_LENGTH.
695fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // In general, this contains the size of mPrimaryKeyCodes, except when this is greater than
705fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // MAX_WORD_LENGTH in which case mPrimaryKeyCodes only contain the first MAX_WORD_LENGTH
715fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // code points.
7201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    private int mCodePointSize;
736a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    private int mCursorPositionWithinWord;
74c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
75923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
760b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether the user chose to capitalize the first char of the word.
77923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
780b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
79923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
80979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public WordComposer() {
815ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard        mCombinerChain = new CombinerChain("");
82f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents = CollectionUtils.newArrayList();
83be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
844b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
85d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
866a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
87d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
8883c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi        mPrevWordsInfo = new PrevWordsInfo(null);
893285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
90923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
91923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
935ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard     * Restart the combiners, possibly with a new spec.
94be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard     * @param combiningSpec The spec string for combining. This is found in the extra value.
95be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard     */
965ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard    public void restartCombining(final String combiningSpec) {
97be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard        final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec;
985ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard        if (!nonNullCombiningSpec.equals(mCombiningSpec)) {
995ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard            mCombinerChain = new CombinerChain(
1005ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard                    mCombinerChain.getComposingWordWithCombiningFeedback().toString(),
1015ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard                    CombinerChain.createCombiners(nonNullCombiningSpec));
102be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard            mCombiningSpec = nonNullCombiningSpec;
103be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard        }
104be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard    }
105be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard
106be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard    /**
107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Clear out the keys registered so far.
108923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
109923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void reset() {
110cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
111f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
112be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
1134a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        mCapsCount = 0;
114e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
115ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = false;
1164b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
117d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
1186a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
119d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
12083c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi        mPrevWordsInfo = new PrevWordsInfo(null);
1213285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
12201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    }
12301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok
1243285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard    private final void refreshTypedWordCache() {
1253285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        mTypedWordCache = mCombinerChain.getComposingWordWithCombiningFeedback();
1263285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        mCodePointSize = Character.codePointCount(mTypedWordCache, 0, mTypedWordCache.length());
127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
128923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
130923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Number of keystrokes in the composing word.
131923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the number of keystrokes
132923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
13370d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard    // This may be made public if need be, but right now it's not used anywhere
13470d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard    /* package for tests */ int size() {
13501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return mCodePointSize;
136923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
137923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1385f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard    /**
1395f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     * Copy the code points in the typed word to a destination array of ints.
1405f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     *
1415f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     * If the array is too small to hold the code points in the typed word, nothing is copied and
1425f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     * -1 is returned.
1435f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     *
1445f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     * @param destination the array of ints.
1455f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     * @return the number of copied code points.
1465f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard     */
1475f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard    public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
14811b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi            final int[] destination) {
14911b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi        // lastIndex is exclusive
15034873a66f03e0b9945474213fa2bc48cc272a7caKeisuke Kuroyanagi        final int lastIndex = mTypedWordCache.length()
15134873a66f03e0b9945474213fa2bc48cc272a7caKeisuke Kuroyanagi                - StringUtils.getTrailingSingleQuotesCount(mTypedWordCache);
15211b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi        if (lastIndex <= 0) {
1535f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard            // The string is empty or contains only single quotes.
1545f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard            return 0;
1555f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard        }
15611b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi
15711b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi        // The following function counts the number of code points in the text range which begins
15811b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi        // at index 0 and extends to the character at lastIndex.
15911b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi        final int codePointSize = Character.codePointCount(mTypedWordCache, 0, lastIndex);
16011b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi        if (codePointSize > destination.length) {
1615f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard            return -1;
1625f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard        }
1635f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard        return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0,
16411b707616800e08891f6b610be90033acda8ffd0Xiaojun Bi                lastIndex, true /* downCase */);
16570d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard    }
16670d9152c7f9e06d7d02dbbcb53af5c8f19d16b8dJean Chalard
1675f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard    public boolean isSingleLetter() {
1685f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard        return size() == 1;
1698e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard    }
1708e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard
171196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    public final boolean isComposingWord() {
17201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return size() > 0;
173196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    }
174196d82cdd740580ed79d801483dbc282be85d076Jean Chalard
17571538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka    public InputPointers getInputPointers() {
17671538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        return mInputPointers;
1778fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1788fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
179bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private static boolean isFirstCharCapitalized(final int index, final int codePoint,
180bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final boolean previous) {
181ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (index == 0) return Character.isUpperCase(codePoint);
182436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka        return previous && !Character.isUpperCase(codePoint);
183ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    }
184ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka
185923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
186d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     * Process an input event.
187d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     *
188d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     * All input events should be supported, including software/hardware events, characters as well
189d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     * as deletions, multiple inputs and gestures.
190d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     *
191d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     * @param event the event to process.
192923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
193d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard    public void processEvent(final Event event) {
194dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int primaryCode = event.mCodePoint;
195dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int keyX = event.mX;
196dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int keyY = event.mY;
197dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int newIndex = size();
1980f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        mCombinerChain.processEvent(mEvents, event);
1990f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        mEvents.add(event);
2000f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        refreshTypedWordCache();
201dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
202dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        // We may have deleted the last one.
203dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        if (0 == mCodePointSize) {
204dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard            mIsFirstCharCapitalized = false;
205dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        }
2061ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        if (Constants.CODE_DELETE != event.mKeyCode) {
207633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            if (newIndex < MAX_WORD_LENGTH) {
208633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                // In the batch input mode, the {@code mInputPointers} holds batch input points and
209633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                // shouldn't be overridden by the "typed key" coordinates
210633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                // (See {@link #setBatchInputWord}).
211633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                if (!mIsBatchMode) {
212633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                    // TODO: Set correct pointer id and time
213633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                    mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
214633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                }
215633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            }
216633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            mIsFirstCharCapitalized = isFirstCharCapitalized(
217633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                    newIndex, primaryCode, mIsFirstCharCapitalized);
218633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            if (Character.isUpperCase(primaryCode)) mCapsCount++;
219633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            if (Character.isDigit(primaryCode)) mDigitsCount++;
2203e160bbe6b91c2d50a410ebd28e10f3e58b2c73aJean Chalard        }
221dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        mAutoCorrection = null;
2220f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard    }
2230f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard
2246a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    public void setCursorPositionWithinWord(final int posWithinWord) {
2256a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = posWithinWord;
226f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        // TODO: compute where that puts us inside the events
2276a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2286a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
2290e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    public boolean isCursorFrontOrMiddleOfComposingWord() {
2300e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (DBG && mCursorPositionWithinWord > mCodePointSize) {
2310e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord
2320e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    + "in a word of size " + mCodePointSize);
2330e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        }
2340e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        return mCursorPositionWithinWord != mCodePointSize;
2356a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2366a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
237f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    /**
238f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * When the cursor is moved by the user, we need to update its position.
239f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * If it falls inside the currently composing word, we don't reset the composition, and
240f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * only update the cursor position.
241f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     *
242f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @param expectedMoveAmount How many java chars to move the cursor. Negative values move
243f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * the cursor backward, positive values move the cursor forward.
244f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @return true if the cursor is still inside the composing word, false otherwise.
245f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     */
246f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) {
247cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        // TODO: should uncommit the composing feedback
248cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
249f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        int actualMoveAmountWithinWord = 0;
250f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        int cursorPos = mCursorPositionWithinWord;
2511ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        // TODO: Don't make that copy. We can do this directly from mTypedWordCache.
2521ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        final int[] codePoints = StringUtils.toCodePointArray(mTypedWordCache);
253f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        if (expectedMoveAmount >= 0) {
254f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor forward for the expected amount or until the end of the word has
255f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // been reached, whichever comes first.
256f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            while (actualMoveAmountWithinWord < expectedMoveAmount && cursorPos < mCodePointSize) {
2575fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard                actualMoveAmountWithinWord += Character.charCount(codePoints[cursorPos]);
258f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                ++cursorPos;
259f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
260f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        } else {
261f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor backward for the expected amount or until the start of the word
262f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // has been reached, whichever comes first.
263f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            while (actualMoveAmountWithinWord > expectedMoveAmount && cursorPos > 0) {
264f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                --cursorPos;
2655fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard                actualMoveAmountWithinWord -= Character.charCount(codePoints[cursorPos]);
266f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
267f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        }
268f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // If the actual and expected amounts differ, we crossed the start or the end of the word
269f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // so the result would not be inside the composing word.
270f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        if (actualMoveAmountWithinWord != expectedMoveAmount) return false;
271f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        mCursorPositionWithinWord = cursorPos;
272f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        return true;
273f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    }
274f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard
275bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputPointers(final InputPointers batchPointers) {
276eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mInputPointers.set(batchPointers);
277d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = true;
278d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
279d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
280bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputWord(final String word) {
281eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        reset();
282eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mIsBatchMode = true;
283eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final int length = word.length();
284eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
285eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int codePoint = Character.codePointAt(word, i);
286eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // We don't want to override the batch input points that are held in mInputPointers
287eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // (See {@link #add(int,int,int)}).
288d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard            processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
289eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
290eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    }
291eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
292923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
2936b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Set the currently composing word to the one passed as an argument.
2946b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
295d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard     * @param codePoints the code points to set as the composing word.
296d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard     * @param coordinates the x, y coordinates of the key in the CoordinateUtils format
297e507d92aa3ee4ae43124c5452f20aa8ed0ecef4cKeisuke Kuroyanagi     * @param prevWordsInfo the information of previous words, to use as context for suggestions
2986b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
299d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard    public void setComposingWord(final int[] codePoints, final int[] coordinates,
300e507d92aa3ee4ae43124c5452f20aa8ed0ecef4cKeisuke Kuroyanagi            final PrevWordsInfo prevWordsInfo) {
3016b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        reset();
302d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard        final int length = codePoints.length;
303d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard        for (int i = 0; i < length; ++i) {
304d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard            processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
305f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard                    CoordinateUtils.xFromArray(coordinates, i),
306f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard                    CoordinateUtils.yFromArray(coordinates, i)));
3076b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
3084b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
309e507d92aa3ee4ae43124c5452f20aa8ed0ecef4cKeisuke Kuroyanagi        mPrevWordsInfo = prevWordsInfo;
3106b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
3116b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
3126b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
313923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the word as it was typed, without any correction applied.
314117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * @return the word that was typed so far. Never returns null.
315923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
3165c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard    public String getTypedWord() {
3173285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        return mTypedWordCache.toString();
318923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
319923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
32083c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi    public PrevWordsInfo getPrevWordsInfoForSuggestion() {
32183c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi        return mPrevWordsInfo;
3222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
324923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
325923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Whether or not the user typed a capital letter as the first letter in the word
326923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return capitalization preference
327923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
3280b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isFirstCharCapitalized() {
3290b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return mIsFirstCharCapitalized;
330923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
3310b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
3320b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    /**
3330b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether or not all of the user typed chars are upper case
3340b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * @return true if all user typed chars are upper case, false otherwise
3350b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     */
3360b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isAllUpperCase() {
337ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        if (size() <= 1) {
338ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard            return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
339ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard                    || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED;
340ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        } else {
341ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard            return mCapsCount == size();
342ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        }
3431eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    }
3441eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard
3451eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    public boolean wasShiftedNoLock() {
3461eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED
3471eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard                || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED;
3480b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    }
3490b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
350923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
3514a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     * Returns true if more than one character is upper case, otherwise returns false.
3524a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     */
3534a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    public boolean isMostlyCaps() {
3544a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        return mCapsCount > 1;
3554a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    }
3561c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
3570fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard    /**
358e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     * Returns true if we have digits in the composing word.
359e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     */
360e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    public boolean hasDigits() {
361e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        return mDigitsCount > 0;
362e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    }
363e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard
364e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    /**
3652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * Saves the caps mode and the previous word at the start of composing.
366adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     *
3672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * WordComposer needs to know about the caps mode for several reasons. The first is, we need
3682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * to know after the fact what the reason was, to register the correct form into the user
3692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * history dictionary: if the word was automatically capitalized, we should insert it in
3702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * all-lower case but if it's a manual pressing of shift, then it should be inserted as is.
371adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * Also, batch input needs to know about the current caps mode to display correctly
372adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * capitalized suggestions.
373adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * @param mode the mode at the time of start
3742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * @param previousWord the previous word as context for suggestions. May be null if none.
3751c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
3762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
3774752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard            final CharSequence previousWord) {
378adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        mCapitalizedMode = mode;
37983c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi        mPrevWordsInfo = new PrevWordsInfo(null == previousWord ? null : previousWord.toString());
3801c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
3811c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
3821c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
3831c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Returns whether the word was automatically capitalized.
3841c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @return whether the word was automatically capitalized
3851c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
386adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public boolean wasAutoCapitalized() {
387adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
388adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard                || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
3891c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
390117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
391117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
392117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * Sets the auto-correction for this word.
393117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
394bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setAutoCorrection(final String correction) {
395be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = correction;
396117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
397117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
398117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
399f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard     * @return the auto-correction for this word, or null if none.
400117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
401bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public String getAutoCorrectionOrNull() {
402be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mAutoCorrection;
403117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
404c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
4054b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    /**
4064b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     * @return whether we started composing this word by resuming suggestion on an existing string
4074b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     */
4084b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    public boolean isResumed() {
4094b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        return mIsResumed;
4104b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    }
4114b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard
412267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard    // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
4134752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard    // committedWord should contain suggestion spans if applicable.
4144752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard    public LastComposedWord commitWord(final int type, final CharSequence committedWord,
415e507d92aa3ee4ae43124c5452f20aa8ed0ecef4cKeisuke Kuroyanagi            final String separatorString, final PrevWordsInfo prevWordsInfo) {
4169271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
4179271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
4189271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // the last composed word to ensure this does not happen.
4191ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        final LastComposedWord lastComposedWord = new LastComposedWord(mEvents,
4203285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard                mInputPointers, mTypedWordCache.toString(), committedWord, separatorString,
421e507d92aa3ee4ae43124c5452f20aa8ed0ecef4cKeisuke Kuroyanagi                prevWordsInfo, mCapitalizedMode);
42271538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.reset();
4239271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
4249271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard                && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
425449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard            lastComposedWord.deactivate();
426449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard        }
427e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mCapsCount = 0;
428e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
429e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mIsBatchMode = false;
43083c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi        mPrevWordsInfo = new PrevWordsInfo(committedWord.toString());
431cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
432f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
4338914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCodePointSize = 0;
434e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mIsFirstCharCapitalized = false;
4358914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCapitalizedMode = CAPS_MODE_OFF;
4363285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
437be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
4386a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
4394b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
440d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
4411f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard        return lastComposedWord;
442c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
443fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard
4447cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // Call this when the recorded previous word should be discarded. This is typically called
4457cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // when the user inputs a separator that's not whitespace (including the case of the
4467cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    // double-space-to-period feature).
4477cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard    public void discardPreviousWordForSuggestion() {
44883c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi        mPrevWordsInfo = new PrevWordsInfo(null);
449fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard    }
450c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
4512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
45283c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi            final PrevWordsInfo prevWordsInfo) {
453f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
454f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        Collections.copy(mEvents, lastComposedWord.mEvents);
45571538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.set(lastComposedWord.mInputPointers);
456cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
4573285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
458b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard        mCapitalizedMode = lastComposedWord.mCapitalizedMode;
459cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
4606a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
461d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
4624b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
46383c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi        mPrevWordsInfo = prevWordsInfo;
4649e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard    }
465d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
466d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    public boolean isBatchMode() {
467d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        return mIsBatchMode;
468d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
469d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
470d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public void setRejectedBatchModeSuggestion(final String rejectedSuggestion) {
471d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = rejectedSuggestion;
472d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
473d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
474d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public String getRejectedBatchModeSuggestion() {
475d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        return mRejectedBatchModeSuggestion;
476d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
477923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
478