WordComposer.java revision be79227dc99421ff7be62224c51c553b3fa73777
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
30fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard *
4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of
6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at
70fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * 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
11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under
14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * 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
199e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalardimport android.text.TextUtils;
209e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard
216b1f500da451de56932a8b2a99c63857994ece85Jean Chalardimport com.android.inputmethod.keyboard.Key;
22887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyDetector;
233708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
24887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport java.util.ArrayList;
26c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalardimport java.util.Arrays;
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 */
31923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpublic class WordComposer {
328fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
33887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka    public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE;
348fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public static final int NOT_A_COORDINATE = -1;
35887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka
36be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    final int N = BinaryDictionary.MAX_WORD_LENGTH;
378fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
38be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    private ArrayList<int[]> mCodes;
39be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    private int[] mXCoordinates;
40be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    private int[] mYCoordinates;
41be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    private StringBuilder mTypedWord;
42be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    private CharSequence mAutoCorrection;
434a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
44be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard    // Cache these values for performance
454a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    private int mCapsCount;
461c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    private boolean mAutoCapitalized;
47117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    private int mTrailingSingleQuotesCount;
48c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
49923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
500b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether the user chose to capitalize the first char of the word.
51923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
520b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
53923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
54979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public WordComposer() {
55be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mCodes = new ArrayList<int[]>(N);
56be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord = new StringBuilder(N);
57be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mXCoordinates = new int[N];
58be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mYCoordinates = new int[N];
59be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
60117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
61923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
62923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
63f733074aaecdfd6e89cfee2daff8a9c1233b60f1satok    public WordComposer(WordComposer source) {
649fbfd5877305ed19a20663630b498b6b3fdae942satok        init(source);
659fbfd5877305ed19a20663630b498b6b3fdae942satok    }
669fbfd5877305ed19a20663630b498b6b3fdae942satok
679fbfd5877305ed19a20663630b498b6b3fdae942satok    public void init(WordComposer source) {
68be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mCodes = new ArrayList<int[]>(source.mCodes);
69be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord = new StringBuilder(source.mTypedWord);
70be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length);
71be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length);
72ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mCapsCount = source.mCapsCount;
73ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
74ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mAutoCapitalized = source.mAutoCapitalized;
75117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
76979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
77979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
78923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
79923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Clear out the keys registered so far.
80923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
81923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void reset() {
82be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mCodes.clear();
83be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.setLength(0);
84be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
854a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        mCapsCount = 0;
86ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = false;
87117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
88923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
89923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
90923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
91923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Number of keystrokes in the composing word.
92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the number of keystrokes
93923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
94ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    public final int size() {
95be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mTypedWord.length();
96923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
97923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
98196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    public final boolean isComposingWord() {
99196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        return size() > 0;
100196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    }
101196d82cdd740580ed79d801483dbc282be85d076Jean Chalard
102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
103923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the codes at a particular position in the word.
104923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param index the position in the word
105923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the unicode for the pressed and surrounding keys
106923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public int[] getCodesAt(int index) {
108be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mCodes.get(index);
109923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
110923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1118fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public int[] getXCoordinates() {
112be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mXCoordinates;
1138fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1148fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
1158fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public int[] getYCoordinates() {
116be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mYCoordinates;
1178fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1188fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
119ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
120ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (index == 0) return Character.isUpperCase(codePoint);
121436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka        return previous && !Character.isUpperCase(codePoint);
122ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    }
123ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka
124923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
125923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of
126923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * the array containing unicode for adjacent keys, sorted by reducing probability/proximity.
127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param codes the array of unicode values
128923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
1298fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public void add(int primaryCode, int[] codes, int x, int y) {
130ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        final int newIndex = size();
131be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.append((char) primaryCode);
132231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        correctPrimaryJuxtapos(primaryCode, codes);
133be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mCodes.add(codes);
134ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
135be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard            mXCoordinates[newIndex] = x;
136be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard            mYCoordinates[newIndex] = y;
1378fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
138ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = isFirstCharCapitalized(
139ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka                newIndex, primaryCode, mIsFirstCharCapitalized);
140ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (Character.isUpperCase(primaryCode)) mCapsCount++;
141117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) {
142117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            ++mTrailingSingleQuotesCount;
143117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        } else {
144117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            mTrailingSingleQuotesCount = 0;
145117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        }
146be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
147923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
148923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
149923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
1506b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Internal method to retrieve reasonable proximity info for a character.
1516b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
1523708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka    private void addKeyInfo(final int codePoint, final Keyboard keyboard,
1536b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final KeyDetector keyDetector) {
1546b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        for (final Key key : keyboard.mKeys) {
1556b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            if (key.mCode == codePoint) {
1566b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                final int x = key.mX + key.mWidth / 2;
1576b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                final int y = key.mY + key.mHeight / 2;
1586b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                final int[] codes = keyDetector.newCodeArray();
159e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                keyDetector.getKeyAndNearbyCodes(x, y, codes);
1606b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                add(codePoint, codes, x, y);
1616b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                return;
1626b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            }
1636b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
1646b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        add(codePoint, new int[] { codePoint },
1656b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
1666b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
1676b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
1686b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
1696b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Set the currently composing word to the one passed as an argument.
1706b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
1716b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
1723708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka    public void setComposingWord(final CharSequence word, final Keyboard keyboard,
1736b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final KeyDetector keyDetector) {
1746b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        reset();
1756b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final int length = word.length();
1766b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        for (int i = 0; i < length; ++i) {
1776b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            int codePoint = word.charAt(i);
1786b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            addKeyInfo(codePoint, keyboard, keyDetector);
1796b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
1806b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
1816b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
1826b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
1836b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Shortcut for the above method, this will create a new KeyDetector for the passed keyboard.
1846b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
1853708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka    public void setComposingWord(final CharSequence word, final Keyboard keyboard) {
1866b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final KeyDetector keyDetector = new KeyDetector(0);
1876b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        keyDetector.setKeyboard(keyboard, 0, 0);
1886b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        keyDetector.setProximityCorrectionEnabled(true);
1896b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        keyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
1906b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        setComposingWord(word, keyboard, keyDetector);
1916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
1926b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
1936b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
194231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * Swaps the first and second values in the codes array if the primary code is not the first
195231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * value in the array but the second. This happens when the preferred key is not the key that
196231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * the user released the finger on.
197231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * @param primaryCode the preferred character
198231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * @param codes array of codes based on distance from touch point
199231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     */
2008fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static void correctPrimaryJuxtapos(int primaryCode, int[] codes) {
201231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        if (codes.length < 2) return;
202231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        if (codes[0] > 0 && codes[1] > 0 && codes[0] != primaryCode && codes[1] == primaryCode) {
203231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani            codes[1] = codes[0];
204231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani            codes[0] = primaryCode;
205231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        }
206231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani    }
207231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani
208231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani    /**
209923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Delete the last keystroke as a result of hitting backspace.
210923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
211923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void deleteLast() {
212ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        final int size = size();
213ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (size > 0) {
214ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            final int lastPos = size - 1;
215be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard            char lastChar = mTypedWord.charAt(lastPos);
216be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard            mCodes.remove(lastPos);
217be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard            mTypedWord.deleteCharAt(lastPos);
218ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            if (Character.isUpperCase(lastChar)) mCapsCount--;
219d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka        }
220ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (size() == 0) {
221ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            mIsFirstCharCapitalized = false;
222117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        }
223117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        if (mTrailingSingleQuotesCount > 0) {
224117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            --mTrailingSingleQuotesCount;
225c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard        } else {
226be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard            for (int i = mTypedWord.length() - 1; i >= 0; --i) {
227be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard                if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break;
228117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard                ++mTrailingSingleQuotesCount;
229117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            }
2308fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
231be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
232923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
233923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
234923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
235923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the word as it was typed, without any correction applied.
236117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * @return the word that was typed so far. Never returns null.
237923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
2385c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard    public String getTypedWord() {
239be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mTypedWord.toString();
240923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
241923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
242923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
243923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Whether or not the user typed a capital letter as the first letter in the word
244923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return capitalization preference
245923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
2460b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isFirstCharCapitalized() {
2470b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return mIsFirstCharCapitalized;
248923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2490b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
250117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    public int trailingSingleQuotesCount() {
251117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        return mTrailingSingleQuotesCount;
252c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard    }
253c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
2540b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    /**
2550b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether or not all of the user typed chars are upper case
2560b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * @return true if all user typed chars are upper case, false otherwise
2570b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     */
2580b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isAllUpperCase() {
2590b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return (mCapsCount > 0) && (mCapsCount == size());
2600b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    }
2610b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
262923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
2634a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     * Returns true if more than one character is upper case, otherwise returns false.
2644a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     */
2654a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    public boolean isMostlyCaps() {
2664a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        return mCapsCount > 1;
2674a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    }
2681c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
2690fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard    /**
2701c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Saves the reason why the word is capitalized - whether it was automatic or
2711c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * due to the user hitting shift in the middle of a sentence.
2721c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @param auto whether it was an automatic capitalization due to start of sentence
2731c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
2741c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    public void setAutoCapitalized(boolean auto) {
2751c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        mAutoCapitalized = auto;
2761c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
2771c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
2781c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
2791c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Returns whether the word was automatically capitalized.
2801c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @return whether the word was automatically capitalized
2811c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
2821c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    public boolean isAutoCapitalized() {
2831c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        return mAutoCapitalized;
2841c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
285117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
286117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
287117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * Sets the auto-correction for this word.
288117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
289117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    public void setAutoCorrection(final CharSequence correction) {
290be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = correction;
291117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
292117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
293117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
294f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard     * @return the auto-correction for this word, or null if none.
295117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
296117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    public CharSequence getAutoCorrectionOrNull() {
297be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        return mAutoCorrection;
298117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
299c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
300267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard    // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
3012692a8700737d8eed268039aa27b22a31669da08Jean Chalard    public LastComposedWord commitWord(final int type) {
3020d0f01da674e89e294d14061837711996dc5a693Ken Wakasa        // Note: currently, we come here whenever we commit a word. If it's any *other* kind than
3030fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // DECIDED_WORD, we should reset mAutoCorrection so that we don't attempt to cancel later.
3040fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // If it's a DECIDED_WORD, it may be an actual auto-correction by the IME, or what the user
3050fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // typed because the IME decided *not* to auto-correct for whatever reason.
3060fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // Ideally we would also null it when it was a DECIDED_WORD that was not an auto-correct.
3070fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // As it happens these two cases should behave differently, because the former can be
3080fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // canceled while the latter can't. Currently, we figure this out in
3092712f23acbb197af3b125da4cc47108e71b7446dJean Chalard        // LastComposedWord#didAutoCorrectToAnotherWord with #equals(). It would be marginally
3102712f23acbb197af3b125da4cc47108e71b7446dJean Chalard        // cleaner to do it here, but it would be slower (since we would #equals() for each commit,
3112712f23acbb197af3b125da4cc47108e71b7446dJean Chalard        // instead of only on cancel), and ultimately we want to figure it out even earlier anyway.
312be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        final LastComposedWord lastComposedWord = new LastComposedWord(type, mCodes,
313be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard                mXCoordinates, mYCoordinates, mTypedWord.toString(),
314be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard                (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD) || (null == mAutoCorrection)
315be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard                        ? null : mAutoCorrection.toString());
316be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mCodes.clear();
317be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.setLength(0);
318be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = null;
3191f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard        return lastComposedWord;
320c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
321c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
3222712f23acbb197af3b125da4cc47108e71b7446dJean Chalard    public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
323be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mCodes = lastComposedWord.mCodes;
324be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mXCoordinates = lastComposedWord.mXCoordinates;
325be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mYCoordinates = lastComposedWord.mYCoordinates;
326be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.setLength(0);
327be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mTypedWord.append(lastComposedWord.mTypedWord);
328be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard        mAutoCorrection = lastComposedWord.mAutoCorrection;
3299e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard    }
330923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
331