WordComposer.java revision 66bb563535dbe3672f99f75bd71763a551444867
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 196b1f500da451de56932a8b2a99c63857994ece85Jean Chalardimport com.android.inputmethod.keyboard.Key; 20887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyDetector; 213708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard; 22887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka 23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport java.util.ArrayList; 24c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalardimport java.util.Arrays; 25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * A place to store the currently composing word with information such as adjacent key codes as well 28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpublic class WordComposer { 308fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 31887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE; 328fbd55229243cb66c03d5ea1f79dfb39f596590dsatok public static final int NOT_A_COORDINATE = -1; 33887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka 34e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok final static int N = BinaryDictionary.MAX_WORD_LENGTH; 358fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 36be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard private ArrayList<int[]> mCodes; 37be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard private int[] mXCoordinates; 38be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard private int[] mYCoordinates; 39be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard private StringBuilder mTypedWord; 40be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard private CharSequence mAutoCorrection; 414a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani 42be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard // Cache these values for performance 434a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani private int mCapsCount; 441c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani private boolean mAutoCapitalized; 45117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard private int mTrailingSingleQuotesCount; 46c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard 47923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 480b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * Whether the user chose to capitalize the first char of the word. 49923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 500b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa private boolean mIsFirstCharCapitalized; 51923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 52979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public WordComposer() { 53be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mCodes = new ArrayList<int[]>(N); 54be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord = new StringBuilder(N); 55be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mXCoordinates = new int[N]; 56be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mYCoordinates = new int[N]; 57be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 58117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 59923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 60923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 61f733074aaecdfd6e89cfee2daff8a9c1233b60f1satok public WordComposer(WordComposer source) { 629fbfd5877305ed19a20663630b498b6b3fdae942satok init(source); 639fbfd5877305ed19a20663630b498b6b3fdae942satok } 649fbfd5877305ed19a20663630b498b6b3fdae942satok 659fbfd5877305ed19a20663630b498b6b3fdae942satok public void init(WordComposer source) { 66be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mCodes = new ArrayList<int[]>(source.mCodes); 67be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord = new StringBuilder(source.mTypedWord); 68be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length); 69be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length); 70ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mCapsCount = source.mCapsCount; 71ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; 72ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mAutoCapitalized = source.mAutoCapitalized; 73117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount; 74979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 75979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 76923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 77923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Clear out the keys registered so far. 78923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 79923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void reset() { 80be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mCodes.clear(); 81be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 82be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 834a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani mCapsCount = 0; 84ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = false; 85117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 86923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 87923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 88923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 89923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Number of keystrokes in the composing word. 90923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @return the number of keystrokes 91923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 92ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka public final int size() { 939159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard return mCodes.size(); 94923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 95923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 96196d82cdd740580ed79d801483dbc282be85d076Jean Chalard public final boolean isComposingWord() { 979159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard return mCodes.size() > 0; 98196d82cdd740580ed79d801483dbc282be85d076Jean Chalard } 99196d82cdd740580ed79d801483dbc282be85d076Jean Chalard 100923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 101923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Returns the codes at a particular position in the word. 102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @param index the position in the word 103923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @return the unicode for the pressed and surrounding keys 104923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 105923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public int[] getCodesAt(int index) { 106be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mCodes.get(index); 107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 108923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 1098fbd55229243cb66c03d5ea1f79dfb39f596590dsatok public int[] getXCoordinates() { 110be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mXCoordinates; 1118fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 1128fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 1138fbd55229243cb66c03d5ea1f79dfb39f596590dsatok public int[] getYCoordinates() { 114be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mYCoordinates; 1158fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 1168fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 117ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) { 118ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (index == 0) return Character.isUpperCase(codePoint); 119436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka return previous && !Character.isUpperCase(codePoint); 120ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka } 121ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka 122923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 123923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of 124923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the array containing unicode for adjacent keys, sorted by reducing probability/proximity. 125923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @param codes the array of unicode values 126923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 1278fbd55229243cb66c03d5ea1f79dfb39f596590dsatok public void add(int primaryCode, int[] codes, int x, int y) { 1289159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int newIndex = mCodes.size(); 1299159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.appendCodePoint(primaryCode); 130231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani correctPrimaryJuxtapos(primaryCode, codes); 131be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mCodes.add(codes); 132ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { 133be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mXCoordinates[newIndex] = x; 134be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mYCoordinates[newIndex] = y; 1358fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 136ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = isFirstCharCapitalized( 137ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka newIndex, primaryCode, mIsFirstCharCapitalized); 138ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (Character.isUpperCase(primaryCode)) mCapsCount++; 139117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) { 140117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard ++mTrailingSingleQuotesCount; 141117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } else { 142117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 143117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 144be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 145923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 146923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 147923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 1486b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * Internal method to retrieve reasonable proximity info for a character. 1496b1f500da451de56932a8b2a99c63857994ece85Jean Chalard */ 1503708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka private void addKeyInfo(final int codePoint, final Keyboard keyboard, 1516b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final KeyDetector keyDetector) { 1526b1f500da451de56932a8b2a99c63857994ece85Jean Chalard for (final Key key : keyboard.mKeys) { 1536b1f500da451de56932a8b2a99c63857994ece85Jean Chalard if (key.mCode == codePoint) { 1546b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final int x = key.mX + key.mWidth / 2; 1556b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final int y = key.mY + key.mHeight / 2; 1566b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final int[] codes = keyDetector.newCodeArray(); 157e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka keyDetector.getKeyAndNearbyCodes(x, y, codes); 1586b1f500da451de56932a8b2a99c63857994ece85Jean Chalard add(codePoint, codes, x, y); 1596b1f500da451de56932a8b2a99c63857994ece85Jean Chalard return; 1606b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 1616b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 1626b1f500da451de56932a8b2a99c63857994ece85Jean Chalard add(codePoint, new int[] { codePoint }, 1636b1f500da451de56932a8b2a99c63857994ece85Jean Chalard WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); 1646b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 1656b1f500da451de56932a8b2a99c63857994ece85Jean Chalard 1666b1f500da451de56932a8b2a99c63857994ece85Jean Chalard /** 1676b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * Set the currently composing word to the one passed as an argument. 1686b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity. 1696b1f500da451de56932a8b2a99c63857994ece85Jean Chalard */ 1703708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka public void setComposingWord(final CharSequence word, final Keyboard keyboard, 1716b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final KeyDetector keyDetector) { 1726b1f500da451de56932a8b2a99c63857994ece85Jean Chalard reset(); 1736b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final int length = word.length(); 1749159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) { 1759159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard int codePoint = Character.codePointAt(word, i); 1766b1f500da451de56932a8b2a99c63857994ece85Jean Chalard addKeyInfo(codePoint, keyboard, keyDetector); 1776b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 1786b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 1796b1f500da451de56932a8b2a99c63857994ece85Jean Chalard 1806b1f500da451de56932a8b2a99c63857994ece85Jean Chalard /** 1816b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * Shortcut for the above method, this will create a new KeyDetector for the passed keyboard. 1826b1f500da451de56932a8b2a99c63857994ece85Jean Chalard */ 1833708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka public void setComposingWord(final CharSequence word, final Keyboard keyboard) { 1846b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final KeyDetector keyDetector = new KeyDetector(0); 1856b1f500da451de56932a8b2a99c63857994ece85Jean Chalard keyDetector.setKeyboard(keyboard, 0, 0); 1866b1f500da451de56932a8b2a99c63857994ece85Jean Chalard keyDetector.setProximityCorrectionEnabled(true); 1876b1f500da451de56932a8b2a99c63857994ece85Jean Chalard keyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth); 1886b1f500da451de56932a8b2a99c63857994ece85Jean Chalard setComposingWord(word, keyboard, keyDetector); 1896b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 1906b1f500da451de56932a8b2a99c63857994ece85Jean Chalard 1916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard /** 192231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani * Swaps the first and second values in the codes array if the primary code is not the first 193231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani * value in the array but the second. This happens when the preferred key is not the key that 194231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani * the user released the finger on. 195231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani * @param primaryCode the preferred character 196231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani * @param codes array of codes based on distance from touch point 197231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani */ 1988fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka private static void correctPrimaryJuxtapos(int primaryCode, int[] codes) { 199231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani if (codes.length < 2) return; 200231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani if (codes[0] > 0 && codes[1] > 0 && codes[0] != primaryCode && codes[1] == primaryCode) { 201231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani codes[1] = codes[0]; 202231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani codes[0] = primaryCode; 203231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani } 204231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani } 205231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani 206231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani /** 207923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Delete the last keystroke as a result of hitting backspace. 208923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 209923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void deleteLast() { 2109159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int size = mCodes.size(); 211ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (size > 0) { 2129159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mCodes.remove(size - 1); 2139159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard // Note: mTypedWord.length() and mCodes.length differ when there are surrogate pairs 2149159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int stringBuilderLength = mTypedWord.length(); 2159159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard if (stringBuilderLength < size) { 2169159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard throw new RuntimeException( 2179159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard "In WordComposer: mCodes and mTypedWords have non-matching lengths"); 2189159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } 2199159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int lastChar = mTypedWord.codePointBefore(stringBuilderLength); 2209159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard if (Character.isSupplementaryCodePoint(lastChar)) { 2219159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.delete(stringBuilderLength - 2, stringBuilderLength); 2229159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } else { 2239159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.deleteCharAt(stringBuilderLength - 1); 2249159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } 225ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (Character.isUpperCase(lastChar)) mCapsCount--; 226d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka } 2279159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard // We may have deleted the last one. 2289159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard if (0 == mCodes.size()) { 229ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = false; 230117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 231117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard if (mTrailingSingleQuotesCount > 0) { 232117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard --mTrailingSingleQuotesCount; 233c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } else { 234825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard int i = mTypedWord.length(); 235825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard while (i > 0) { 236825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard i = mTypedWord.offsetByCodePoints(i, -1); 237be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; 238117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard ++mTrailingSingleQuotesCount; 239117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 2408fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 241be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 242923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 243923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 244923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 245923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Returns the word as it was typed, without any correction applied. 246117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard * @return the word that was typed so far. Never returns null. 247923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 2485c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard public String getTypedWord() { 249be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mTypedWord.toString(); 250923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 251923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 252923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 253923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Whether or not the user typed a capital letter as the first letter in the word 254923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @return capitalization preference 255923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 2560b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa public boolean isFirstCharCapitalized() { 2570b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa return mIsFirstCharCapitalized; 258923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 2590b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa 260117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard public int trailingSingleQuotesCount() { 261117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard return mTrailingSingleQuotesCount; 262c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } 263c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard 2640b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa /** 2650b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * Whether or not all of the user typed chars are upper case 2660b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * @return true if all user typed chars are upper case, false otherwise 2670b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa */ 2680b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa public boolean isAllUpperCase() { 2690b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa return (mCapsCount > 0) && (mCapsCount == size()); 2700b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa } 2710b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa 272923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 2734a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani * Returns true if more than one character is upper case, otherwise returns false. 2744a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani */ 2754a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani public boolean isMostlyCaps() { 2764a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani return mCapsCount > 1; 2774a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani } 2781c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani 2790fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard /** 2801c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * Saves the reason why the word is capitalized - whether it was automatic or 2811c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * due to the user hitting shift in the middle of a sentence. 2821c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * @param auto whether it was an automatic capitalization due to start of sentence 2831c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani */ 2841c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani public void setAutoCapitalized(boolean auto) { 2851c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani mAutoCapitalized = auto; 2861c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani } 2871c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani 2881c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani /** 2891c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * Returns whether the word was automatically capitalized. 2901c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * @return whether the word was automatically capitalized 2911c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani */ 2921c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani public boolean isAutoCapitalized() { 2931c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani return mAutoCapitalized; 2941c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani } 295117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard 296117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard /** 297117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard * Sets the auto-correction for this word. 298117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard */ 299117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard public void setAutoCorrection(final CharSequence correction) { 300be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = correction; 301117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard } 302117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard 303117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard /** 304f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard * @return the auto-correction for this word, or null if none. 305117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard */ 306117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard public CharSequence getAutoCorrectionOrNull() { 307be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mAutoCorrection; 308117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard } 309c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard 310267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above. 31166bb563535dbe3672f99f75bd71763a551444867Jean Chalard public LastComposedWord commitWord(final int type, final String committedWord, 31266bb563535dbe3672f99f75bd71763a551444867Jean Chalard final int separatorCode) { 3130d0f01da674e89e294d14061837711996dc5a693Ken Wakasa // Note: currently, we come here whenever we commit a word. If it's any *other* kind than 314cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard // DECIDED_WORD, we should deactivate the last composed word so that we don't attempt to 315cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard // cancel later. 3160fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard // If it's a DECIDED_WORD, it may be an actual auto-correction by the IME, or what the user 3170fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard // typed because the IME decided *not* to auto-correct for whatever reason. 3180fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard // Ideally we would also null it when it was a DECIDED_WORD that was not an auto-correct. 3190fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard // As it happens these two cases should behave differently, because the former can be 3200fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard // canceled while the latter can't. Currently, we figure this out in 3212712f23acbb197af3b125da4cc47108e71b7446dJean Chalard // LastComposedWord#didAutoCorrectToAnotherWord with #equals(). It would be marginally 3222712f23acbb197af3b125da4cc47108e71b7446dJean Chalard // cleaner to do it here, but it would be slower (since we would #equals() for each commit, 3232712f23acbb197af3b125da4cc47108e71b7446dJean Chalard // instead of only on cancel), and ultimately we want to figure it out even earlier anyway. 324a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard final ArrayList<int[]> codes = mCodes; 325a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard final int[] xCoordinates = mXCoordinates; 326a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard final int[] yCoordinates = mYCoordinates; 327a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard mCodes = new ArrayList<int[]>(N); 328a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard mXCoordinates = new int[N]; 329a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard mYCoordinates = new int[N]; 330a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard final LastComposedWord lastComposedWord = new LastComposedWord(codes, 33166bb563535dbe3672f99f75bd71763a551444867Jean Chalard xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode); 332449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD) { 333449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard lastComposedWord.deactivate(); 334449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard } 335be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 336be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 3371f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard return lastComposedWord; 338c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard } 339c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard 3402712f23acbb197af3b125da4cc47108e71b7446dJean Chalard public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) { 341be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mCodes = lastComposedWord.mCodes; 342be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mXCoordinates = lastComposedWord.mXCoordinates; 343be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mYCoordinates = lastComposedWord.mYCoordinates; 344be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 345be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.append(lastComposedWord.mTypedWord); 346cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard mAutoCorrection = null; // This will be filled by the next call to updateSuggestion. 3479e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard } 348923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 349