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
19b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheikimport com.android.inputmethod.annotations.UsedForTesting;
207ede642df417c0f732573f639970b138f0bee18cJean Chalardimport com.android.inputmethod.event.CombinerChain;
21f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport com.android.inputmethod.event.Event;
22176ad53c6fee47df7595e7501e7e2829d990364dJean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
23ecab6aff5908bfd5b34670d2e2bb3696627fa47cJean Chalardimport com.android.inputmethod.latin.common.ComposedData;
249342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport com.android.inputmethod.latin.common.Constants;
255b91b551e5ffaf2c2e691dfbd434f21c82293986Jean Chalardimport com.android.inputmethod.latin.common.CoordinateUtils;
2636799b2aa2982ec17341cd2c5ed81e608bcee8c6Jean Chalardimport com.android.inputmethod.latin.common.InputPointers;
274beeb9253a06482299e0c67467531d30436a02fcJean Chalardimport com.android.inputmethod.latin.common.StringUtils;
282dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaokaimport com.android.inputmethod.latin.define.DebugFlags;
290f7d881dc72132dfd75c8b4fe61a69fc5cdcd460Mohammadinamul Sheikimport com.android.inputmethod.latin.define.DecoderSpecificConstants;
30887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka
31f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport java.util.ArrayList;
32f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport java.util.Collections;
33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
347f545a57c987862d55966ac08ef64cfe0b9f51e4Ken Wakasaimport javax.annotation.Nonnull;
357f545a57c987862d55966ac08ef64cfe0b9f51e4Ken Wakasa
36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * A place to store the currently composing word with information such as adjacent key codes as well
38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
39a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class WordComposer {
400f7d881dc72132dfd75c8b4fe61a69fc5cdcd460Mohammadinamul Sheik    private static final int MAX_WORD_LENGTH = DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH;
412dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaoka    private static final boolean DBG = DebugFlags.DEBUG_ENABLED;
428fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
43adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_OFF = 0;
44adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
45adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // aren't used anywhere in the code
46adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1;
47adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3;
48adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_AUTO_SHIFTED = 0x5;
49adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
50adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard
517ede642df417c0f732573f639970b138f0bee18cJean Chalard    private CombinerChain mCombinerChain;
52be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard    private String mCombiningSpec; // Memory so that we don't uselessly recreate the combiner chain
537ede642df417c0f732573f639970b138f0bee18cJean Chalard
54f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard    // The list of events that served to compose this string.
55f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard    private final ArrayList<Event> mEvents;
5696b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa    private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
57176ad53c6fee47df7595e7501e7e2829d990364dJean Chalard    private SuggestedWordInfo mAutoCorrection;
584b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    private boolean mIsResumed;
59d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    private boolean mIsBatchMode;
60d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user
61d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // gestures a word, is displeased with the results and hits backspace, then gestures again.
62d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // At the very least we should avoid re-suggesting the same thing, and to do that we memorize
63d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // the rejected suggestion in this variable.
64d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // TODO: this should be done in a comprehensive way by the User History feature instead of
65d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    // as an ad-hockery here.
66d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    private String mRejectedBatchModeSuggestion;
674a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
68be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    // Cache these values for performance
693285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard    private CharSequence mTypedWordCache;
704a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    private int mCapsCount;
71e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    private int mDigitsCount;
72adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    private int mCapitalizedMode;
735fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // This is the number of code points entered so far. This is not limited to MAX_WORD_LENGTH.
745fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // In general, this contains the size of mPrimaryKeyCodes, except when this is greater than
755fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // MAX_WORD_LENGTH in which case mPrimaryKeyCodes only contain the first MAX_WORD_LENGTH
765fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard    // code points.
7701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    private int mCodePointSize;
786a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    private int mCursorPositionWithinWord;
79c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
80923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
81367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard     * Whether the composing word has the only first char capitalized.
82923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
83367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard    private boolean mIsOnlyFirstCharCapitalized;
84923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
85979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public WordComposer() {
865ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard        mCombinerChain = new CombinerChain("");
87a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        mEvents = new ArrayList<>();
88be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
894b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
90d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
916a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
92d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
933285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
94923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
95923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
96ecab6aff5908bfd5b34670d2e2bb3696627fa47cJean Chalard    public ComposedData getComposedDataSnapshot() {
97ecab6aff5908bfd5b34670d2e2bb3696627fa47cJean Chalard        return new ComposedData(getInputPointers(), isBatchMode(), mTypedWordCache.toString());
98ecab6aff5908bfd5b34670d2e2bb3696627fa47cJean Chalard    }
99ecab6aff5908bfd5b34670d2e2bb3696627fa47cJean Chalard
100923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
1015ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard     * Restart the combiners, possibly with a new spec.
102be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard     * @param combiningSpec The spec string for combining. This is found in the extra value.
103be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard     */
1045ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard    public void restartCombining(final String combiningSpec) {
105be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard        final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec;
1065ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard        if (!nonNullCombiningSpec.equals(mCombiningSpec)) {
1075ce39dfa782031fd53af167c0fd9a9bc63d21149Jean Chalard            mCombinerChain = new CombinerChain(
108e1758feeffa388f2b75040e8f54b466c3591a148Dan Zivkovic                    mCombinerChain.getComposingWordWithCombiningFeedback().toString());
109be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard            mCombiningSpec = nonNullCombiningSpec;
110be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard        }
111be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard    }
112be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard
113be99616afa2243fe48dc406d0a3f442cb05453b4Jean Chalard    /**
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() {
117cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
118f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
119be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
1204a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        mCapsCount = 0;
121e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
122367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard        mIsOnlyFirstCharCapitalized = false;
1234b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
124d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = false;
1256a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
126d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
1273285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
12801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok    }
12901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok
1303285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard    private final void refreshTypedWordCache() {
1313285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        mTypedWordCache = mCombinerChain.getComposingWordWithCombiningFeedback();
1323285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        mCodePointSize = Character.codePointCount(mTypedWordCache, 0, mTypedWordCache.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     */
139c92c883fdf2287b49392692fa2e8d109dc26f785David Faden    public int size() {
14001ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return mCodePointSize;
141923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
142923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1435f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard    public boolean isSingleLetter() {
1445f430e0189f6c0a1a34edb0e00809bc53a525a7bJean Chalard        return size() == 1;
1458e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard    }
1468e829c37dff9fb5eec62390bf0e2c6f12fd6781bJean Chalard
147196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    public final boolean isComposingWord() {
14801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok        return size() > 0;
149196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    }
150196d82cdd740580ed79d801483dbc282be85d076Jean Chalard
15171538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka    public InputPointers getInputPointers() {
15271538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        return mInputPointers;
1538fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1548fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
155923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
1567196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard     * Process an event and return an event, and return a processed event to apply.
1577196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard     * @param event the unprocessed event.
1588e38b12e9ccc48bcb18b2eeec4d53d19cf7a29c9Jean Chalard     * @return the processed event. Never null, but may be marked as consumed.
1597196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard     */
1607f545a57c987862d55966ac08ef64cfe0b9f51e4Ken Wakasa    @Nonnull
161d3a4c5132422b189c8dbb94dbbe84a9b9761b0a8Tadashi G. Takaoka    public Event processEvent(@Nonnull final Event event) {
1627196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard        final Event processedEvent = mCombinerChain.processEvent(mEvents, event);
163e0bad8e988a23553181fb670f8a2589a79f22c40Jean Chalard        // The retained state of the combiner chain may have changed while processing the event,
164e0bad8e988a23553181fb670f8a2589a79f22c40Jean Chalard        // so we need to update our cache.
165e0bad8e988a23553181fb670f8a2589a79f22c40Jean Chalard        refreshTypedWordCache();
1667196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard        mEvents.add(event);
1677196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard        return processedEvent;
1687196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard    }
1697196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard
1707196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard    /**
1717196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard     * Apply a processed input event.
172d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     *
173d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     * All input events should be supported, including software/hardware events, characters as well
174d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     * as deletions, multiple inputs and gestures.
175d52bec00695a6e43b8e8836112919c02952d4dccJean Chalard     *
1768e38b12e9ccc48bcb18b2eeec4d53d19cf7a29c9Jean Chalard     * @param event the event to apply. Must not be null.
177923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
1787196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard    public void applyProcessedEvent(final Event event) {
1798e38b12e9ccc48bcb18b2eeec4d53d19cf7a29c9Jean Chalard        mCombinerChain.applyProcessedEvent(event);
180dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int primaryCode = event.mCodePoint;
181dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int keyX = event.mX;
182dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int keyY = event.mY;
183dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        final int newIndex = size();
1840f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard        refreshTypedWordCache();
185dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
186dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        // We may have deleted the last one.
187dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        if (0 == mCodePointSize) {
188367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard            mIsOnlyFirstCharCapitalized = false;
189dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        }
1901ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        if (Constants.CODE_DELETE != event.mKeyCode) {
191633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            if (newIndex < MAX_WORD_LENGTH) {
192633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                // In the batch input mode, the {@code mInputPointers} holds batch input points and
193633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                // shouldn't be overridden by the "typed key" coordinates
194633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                // (See {@link #setBatchInputWord}).
195633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                if (!mIsBatchMode) {
196633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                    // TODO: Set correct pointer id and time
197633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                    mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
198633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard                }
199633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            }
200367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard            if (0 == newIndex) {
201367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard                mIsOnlyFirstCharCapitalized = Character.isUpperCase(primaryCode);
202367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard            } else {
203367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard                mIsOnlyFirstCharCapitalized = mIsOnlyFirstCharCapitalized
204367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard                        && !Character.isUpperCase(primaryCode);
205367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard            }
206633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            if (Character.isUpperCase(primaryCode)) mCapsCount++;
207633e4f45330cf32d76c29d999b723ddeafd3a90cJean Chalard            if (Character.isDigit(primaryCode)) mDigitsCount++;
2083e160bbe6b91c2d50a410ebd28e10f3e58b2c73aJean Chalard        }
209dfaeb125eb27172e4d38e1e5a87a13a3320c1e82Jean Chalard        mAutoCorrection = null;
2100f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard    }
2110f913ff5ba71c40a4492994a23010336cd25be8eJean Chalard
2126a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    public void setCursorPositionWithinWord(final int posWithinWord) {
2136a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = posWithinWord;
214f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        // TODO: compute where that puts us inside the events
2156a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2166a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
2170e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard    public boolean isCursorFrontOrMiddleOfComposingWord() {
2180e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (DBG && mCursorPositionWithinWord > mCodePointSize) {
2190e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord
2200e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    + "in a word of size " + mCodePointSize);
2210e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        }
2220e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        return mCursorPositionWithinWord != mCodePointSize;
2236a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
2246a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
225f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    /**
226f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * When the cursor is moved by the user, we need to update its position.
227f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * If it falls inside the currently composing word, we don't reset the composition, and
228f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * only update the cursor position.
229f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     *
230f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @param expectedMoveAmount How many java chars to move the cursor. Negative values move
231f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * the cursor backward, positive values move the cursor forward.
232f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     * @return true if the cursor is still inside the composing word, false otherwise.
233f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard     */
234f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) {
23569c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic        int actualMoveAmount = 0;
236f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        int cursorPos = mCursorPositionWithinWord;
2371ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        // TODO: Don't make that copy. We can do this directly from mTypedWordCache.
2381ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        final int[] codePoints = StringUtils.toCodePointArray(mTypedWordCache);
239f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        if (expectedMoveAmount >= 0) {
240f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor forward for the expected amount or until the end of the word has
241f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // been reached, whichever comes first.
24269c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic            while (actualMoveAmount < expectedMoveAmount && cursorPos < codePoints.length) {
24369c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic                actualMoveAmount += Character.charCount(codePoints[cursorPos]);
244f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                ++cursorPos;
245f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
246f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        } else {
247f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // Moving the cursor backward for the expected amount or until the start of the word
248f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            // has been reached, whichever comes first.
24969c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic            while (actualMoveAmount > expectedMoveAmount && cursorPos > 0) {
250f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard                --cursorPos;
25169c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic                actualMoveAmount -= Character.charCount(codePoints[cursorPos]);
252f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard            }
253f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        }
254f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // If the actual and expected amounts differ, we crossed the start or the end of the word
255f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        // so the result would not be inside the composing word.
25669c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic        if (actualMoveAmount != expectedMoveAmount) {
25769c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic            return false;
25869c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic        }
259f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        mCursorPositionWithinWord = cursorPos;
26069c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic        mCombinerChain.applyProcessedEvent(mCombinerChain.processEvent(
26169c04cadc7c017f9de53f13ea045347b80316d4aDan Zivkovic                mEvents, Event.createCursorMovedEvent(cursorPos)));
262f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard        return true;
263f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard    }
264f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard
265bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputPointers(final InputPointers batchPointers) {
266eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mInputPointers.set(batchPointers);
267d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        mIsBatchMode = true;
268d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
269d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
270bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setBatchInputWord(final String word) {
271eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        reset();
272eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        mIsBatchMode = true;
273eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final int length = word.length();
274eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
275eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int codePoint = Character.codePointAt(word, i);
276eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // We don't want to override the batch input points that are held in mInputPointers
277eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // (See {@link #add(int,int,int)}).
2787196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard            final Event processedEvent =
2797196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard                    processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
2807196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard            applyProcessedEvent(processedEvent);
281eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
282eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    }
283eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
284923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
2856b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Set the currently composing word to the one passed as an argument.
2866b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
287d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard     * @param codePoints the code points to set as the composing word.
288d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard     * @param coordinates the x, y coordinates of the key in the CoordinateUtils format
2896b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
290367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard    public void setComposingWord(final int[] codePoints, final int[] coordinates) {
2916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        reset();
292d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard        final int length = codePoints.length;
293d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard        for (int i = 0; i < length; ++i) {
2947196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard            final Event processedEvent =
2957196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard                    processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
296f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard                    CoordinateUtils.xFromArray(coordinates, i),
297f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard                    CoordinateUtils.yFromArray(coordinates, i)));
2987196566d4f2048a4160acc6271ccb26c73d6fcd6Jean Chalard            applyProcessedEvent(processedEvent);
2996b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
3004b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
3016b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
3026b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
3036b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
304923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the word as it was typed, without any correction applied.
305117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * @return the word that was typed so far. Never returns null.
306923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
3075c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard    public String getTypedWord() {
3083285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        return mTypedWordCache.toString();
309923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
310923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
311923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
312deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * Whether this composer is composing or about to compose a word in which only the first letter
313deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * is a capital.
314deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     *
315deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * If we do have a composing word, we just return whether the word has indeed only its first
316deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * character capitalized. If we don't, then we return a value based on the capitalized mode,
317deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * which tell us what is likely to happen for the next composing word.
318deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     *
319923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return capitalization preference
320923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
321deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard    public boolean isOrWillBeOnlyFirstCharCapitalized() {
322deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard        return isComposingWord() ? mIsOnlyFirstCharCapitalized
323deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard                : (CAPS_MODE_OFF != mCapitalizedMode);
324923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
3250b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
3260b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    /**
3270b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether or not all of the user typed chars are upper case
3280b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * @return true if all user typed chars are upper case, false otherwise
3290b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     */
3300b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isAllUpperCase() {
331ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        if (size() <= 1) {
332ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard            return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
333ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard                    || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED;
334ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard        }
3355f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka        return mCapsCount == size();
3361eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    }
3371eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard
3381eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard    public boolean wasShiftedNoLock() {
3391eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED
3401eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard                || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED;
3410b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    }
3420b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
343923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
3444a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     * Returns true if more than one character is upper case, otherwise returns false.
3454a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     */
3464a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    public boolean isMostlyCaps() {
3474a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        return mCapsCount > 1;
3484a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    }
3491c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
3500fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard    /**
351e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     * Returns true if we have digits in the composing word.
352e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard     */
353e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    public boolean hasDigits() {
354e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        return mDigitsCount > 0;
355e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    }
356e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard
357e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard    /**
358367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard     * Saves the caps mode at the start of composing.
359adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     *
3602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * WordComposer needs to know about the caps mode for several reasons. The first is, we need
3612fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * to know after the fact what the reason was, to register the correct form into the user
3622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * history dictionary: if the word was automatically capitalized, we should insert it in
3632fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa     * all-lower case but if it's a manual pressing of shift, then it should be inserted as is.
364adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * Also, batch input needs to know about the current caps mode to display correctly
365adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * capitalized suggestions.
366adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard     * @param mode the mode at the time of start
3671c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
368367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard    public void setCapitalizedModeAtStartComposingTime(final int mode) {
369adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        mCapitalizedMode = mode;
3701c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
3711c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
3721c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
373deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * Before fetching suggestions, we don't necessarily know about the capitalized mode yet.
374deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     *
375deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * If we don't have a composing word yet, we take a note of this mode so that we can then
376deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * supply this information to the suggestion process. If we have a composing word, then
377deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * the previous mode has priority over this.
378deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     * @param mode the mode just before fetching suggestions
379deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard     */
380deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard    public void adviseCapitalizedModeBeforeFetchingSuggestions(final int mode) {
381deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard        if (!isComposingWord()) {
382deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard            mCapitalizedMode = mode;
383deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard        }
384deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard    }
385deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard
386deccc23564304b11d58590041f25adffdf6e1b6aJean Chalard    /**
3871c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Returns whether the word was automatically capitalized.
3881c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @return whether the word was automatically capitalized
3891c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
390adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    public boolean wasAutoCapitalized() {
391adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
392adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard                || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
3931c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
394117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
395117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
396117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * Sets the auto-correction for this word.
397117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
398176ad53c6fee47df7595e7501e7e2829d990364dJean Chalard    public void setAutoCorrection(final SuggestedWordInfo autoCorrection) {
399176ad53c6fee47df7595e7501e7e2829d990364dJean Chalard        mAutoCorrection = autoCorrection;
400117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
401117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
402117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
403f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard     * @return the auto-correction for this word, or null if none.
404117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
405176ad53c6fee47df7595e7501e7e2829d990364dJean Chalard    public SuggestedWordInfo getAutoCorrectionOrNull() {
406be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mAutoCorrection;
407117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
408c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
4094b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    /**
4104b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     * @return whether we started composing this word by resuming suggestion on an existing string
4114b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard     */
4124b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    public boolean isResumed() {
4134b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        return mIsResumed;
4144b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard    }
4154b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard
416267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard    // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
4174752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard    // committedWord should contain suggestion spans if applicable.
4184752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard    public LastComposedWord commitWord(final int type, final CharSequence committedWord,
419bb0eca57054758ef17b032d2654c1fc5f6b32101Keisuke Kuroyanagi            final String separatorString, final NgramContext ngramContext) {
4209271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
4219271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
4229271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        // the last composed word to ensure this does not happen.
4231ef9d5938e671ce7a9d5c29d95c1364d6d6249eaJean Chalard        final LastComposedWord lastComposedWord = new LastComposedWord(mEvents,
4243285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard                mInputPointers, mTypedWordCache.toString(), committedWord, separatorString,
425bb0eca57054758ef17b032d2654c1fc5f6b32101Keisuke Kuroyanagi                ngramContext, mCapitalizedMode);
42671538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.reset();
4279271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard        if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
4289271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard                && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
429449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard            lastComposedWord.deactivate();
430449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard        }
431e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mCapsCount = 0;
432e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard        mDigitsCount = 0;
433e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard        mIsBatchMode = false;
434cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
435f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
4368914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCodePointSize = 0;
437367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard        mIsOnlyFirstCharCapitalized = false;
4388914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard        mCapitalizedMode = CAPS_MODE_OFF;
4393285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
440be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
4416a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = 0;
4424b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = false;
443d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
4441f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard        return lastComposedWord;
445c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
446fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard
447367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard    public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
448f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        mEvents.clear();
449f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard        Collections.copy(mEvents, lastComposedWord.mEvents);
45071538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka        mInputPointers.set(lastComposedWord.mInputPointers);
451cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard        mCombinerChain.reset();
4523285b68c97d966d86c08aeb837e5bf5633981357Jean Chalard        refreshTypedWordCache();
453b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard        mCapitalizedMode = lastComposedWord.mCapitalizedMode;
454cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
4556a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mCursorPositionWithinWord = mCodePointSize;
456d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = null;
4574b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard        mIsResumed = true;
4589e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard    }
459d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka
460d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    public boolean isBatchMode() {
461d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka        return mIsBatchMode;
462d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka    }
463d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
464d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public void setRejectedBatchModeSuggestion(final String rejectedSuggestion) {
465d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        mRejectedBatchModeSuggestion = rejectedSuggestion;
466d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
467d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard
468d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    public String getRejectedBatchModeSuggestion() {
469d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard        return mRejectedBatchModeSuggestion;
470d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard    }
471b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik
472b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik    @UsedForTesting
473b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik    void addInputPointerForTest(int index, int keyX, int keyY) {
474b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik        mInputPointers.addPointerAt(index, keyX, keyY, 0, 0);
475b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik    }
476b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik
477b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik    @UsedForTesting
478b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik    void setTypedWordCacheForTests(String typedWordCacheForTests) {
479b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik        mTypedWordCache = typedWordCacheForTests;
480b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik    }
481923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
482