WordComposer.java revision a28a05e971cc242b338331a3b78276fa95188d19
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; 203708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard; 21887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka 22c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalardimport java.util.Arrays; 23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * A place to store the currently composing word with information such as adjacent key codes as well 26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 27a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class WordComposer { 2801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok private static final int N = BinaryDictionary.MAX_WORD_LENGTH; 298fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 30adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_OFF = 0; 31adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits 32adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard // aren't used anywhere in the code 33adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1; 34adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3; 35adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_AUTO_SHIFTED = 0x5; 36adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7; 37adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard 3801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok private int[] mPrimaryKeyCodes; 3957f7de0ba664187e13bcea5adff7f5f65eddd823Tadashi G. Takaoka private final InputPointers mInputPointers = new InputPointers(N); 406b4ce58fc6216b9befd0567b56522ee32f2471a2Tadashi G. Takaoka private final StringBuilder mTypedWord; 41be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard private CharSequence mAutoCorrection; 424b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard private boolean mIsResumed; 43d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka private boolean mIsBatchMode; 444a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani 45be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard // Cache these values for performance 464a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani private int mCapsCount; 47e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard private int mDigitsCount; 48adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard private int mCapitalizedMode; 49117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard private int mTrailingSingleQuotesCount; 5001ab7c8b59a7f12862fbd95fb252e56719f1757fsatok private int mCodePointSize; 51c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard 52923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 530b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * Whether the user chose to capitalize the first char of the word. 54923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 550b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa private boolean mIsFirstCharCapitalized; 56923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 57979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public WordComposer() { 5801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mPrimaryKeyCodes = new int[N]; 59be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord = new StringBuilder(N); 60be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 61117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 624b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = false; 63d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = false; 6401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 65923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 66923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 67f733074aaecdfd6e89cfee2daff8a9c1233b60f1satok public WordComposer(WordComposer source) { 6801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length); 69be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord = new StringBuilder(source.mTypedWord); 7071538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka mInputPointers.copy(source.mInputPointers); 71ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mCapsCount = source.mCapsCount; 72e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard mDigitsCount = source.mDigitsCount; 73ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; 74adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard mCapitalizedMode = source.mCapitalizedMode; 75117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount; 764b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = source.mIsResumed; 77d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = source.mIsBatchMode; 7801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 79979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 80979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 81923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 82923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Clear out the keys registered so far. 83923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 84923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void reset() { 85be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 86be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 874a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani mCapsCount = 0; 88e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard mDigitsCount = 0; 89ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = false; 90117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 914b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = false; 92d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = false; 9301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 9401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok } 9501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok 96e55c23e4b0b8d9d66349a3b275d0fa1540d7450aKen Wakasa private final void refreshSize() { 9701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length()); 98923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 99923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 100923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 101923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Number of keystrokes in the composing word. 102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @return the number of keystrokes 103923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 104ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka public final int size() { 10501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok return mCodePointSize; 106923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 108196d82cdd740580ed79d801483dbc282be85d076Jean Chalard public final boolean isComposingWord() { 10901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok return size() > 0; 110196d82cdd740580ed79d801483dbc282be85d076Jean Chalard } 111196d82cdd740580ed79d801483dbc282be85d076Jean Chalard 1129611b281e18ac71d825ff5bc771a111423772cb3satok // TODO: make sure that the index should not exceed MAX_WORD_LENGTH 11301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok public int getCodeAt(int index) { 1149611b281e18ac71d825ff5bc771a111423772cb3satok if (index >= BinaryDictionary.MAX_WORD_LENGTH) { 1159611b281e18ac71d825ff5bc771a111423772cb3satok return -1; 1169611b281e18ac71d825ff5bc771a111423772cb3satok } 11701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok return mPrimaryKeyCodes[index]; 118923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 119923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 12071538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka public InputPointers getInputPointers() { 12171538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka return mInputPointers; 1228fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 1238fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 124ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) { 125ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (index == 0) return Character.isUpperCase(codePoint); 126436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka return previous && !Character.isUpperCase(codePoint); 127ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka } 128ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka 129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 130c61cd79229b1871d0f603a23389695d7f7751e66Tadashi G. Takaoka * Add a new keystroke, with the pressed key's code point with the touch point coordinates. 131923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 1325c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka public void add(int primaryCode, int keyX, int keyY) { 13301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok final int newIndex = size(); 1349159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.appendCodePoint(primaryCode); 13501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 136ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { 13767094f5bdece00994f70c6f1fa9a6ff7b8f3c3c1satok mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE 13867094f5bdece00994f70c6f1fa9a6ff7b8f3c3c1satok ? Character.toLowerCase(primaryCode) : primaryCode; 139eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // In the batch input mode, the {@code mInputPointers} holds batch input points and 140eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // shouldn't be overridden by the "typed key" coordinates 141eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (See {@link #setBatchInputWord}). 142eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang if (!mIsBatchMode) { 143eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // TODO: Set correct pointer id and time 144eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0); 145eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 1468fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 147ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = isFirstCharCapitalized( 148ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka newIndex, primaryCode, mIsFirstCharCapitalized); 149ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (Character.isUpperCase(primaryCode)) mCapsCount++; 150e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard if (Character.isDigit(primaryCode)) mDigitsCount++; 151117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) { 152117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard ++mTrailingSingleQuotesCount; 153117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } else { 154117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 155117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 156be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 157923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 158923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 159d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka public void setBatchInputPointers(InputPointers batchPointers) { 160eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang mInputPointers.set(batchPointers); 161d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = true; 162d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 163d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 164eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang public void setBatchInputWord(CharSequence word) { 165eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang reset(); 166eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang mIsBatchMode = true; 167eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int length = word.length(); 168eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) { 169eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int codePoint = Character.codePointAt(word, i); 170eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // We don't want to override the batch input points that are held in mInputPointers 171eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (See {@link #add(int,int,int)}). 172ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); 173eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 174eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 175eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang 176923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 1776b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * Internal method to retrieve reasonable proximity info for a character. 1786b1f500da451de56932a8b2a99c63857994ece85Jean Chalard */ 179a492790982c6d7df62f66344db30b31995800e1bJean Chalard private void addKeyInfo(final int codePoint, final Keyboard keyboard) { 180adc80eef1533189ca2f3bcb08126d4db3f5bfbbdTadashi G. Takaoka final Key key = keyboard.getKey(codePoint); 181adc80eef1533189ca2f3bcb08126d4db3f5bfbbdTadashi G. Takaoka if (key != null) { 182adc80eef1533189ca2f3bcb08126d4db3f5bfbbdTadashi G. Takaoka final int x = key.mX + key.mWidth / 2; 183adc80eef1533189ca2f3bcb08126d4db3f5bfbbdTadashi G. Takaoka final int y = key.mY + key.mHeight / 2; 184adc80eef1533189ca2f3bcb08126d4db3f5bfbbdTadashi G. Takaoka add(codePoint, x, y); 185adc80eef1533189ca2f3bcb08126d4db3f5bfbbdTadashi G. Takaoka return; 1866b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 187ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); 1886b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 1896b1f500da451de56932a8b2a99c63857994ece85Jean Chalard 1906b1f500da451de56932a8b2a99c63857994ece85Jean Chalard /** 1916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * Set the currently composing word to the one passed as an argument. 1926b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity. 1936b1f500da451de56932a8b2a99c63857994ece85Jean Chalard */ 194a492790982c6d7df62f66344db30b31995800e1bJean Chalard public void setComposingWord(final CharSequence word, final Keyboard keyboard) { 1956b1f500da451de56932a8b2a99c63857994ece85Jean Chalard reset(); 1966b1f500da451de56932a8b2a99c63857994ece85Jean Chalard final int length = word.length(); 1979159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) { 1989159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard int codePoint = Character.codePointAt(word, i); 199a492790982c6d7df62f66344db30b31995800e1bJean Chalard addKeyInfo(codePoint, keyboard); 2006b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 2014b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = true; 2026b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 2036b1f500da451de56932a8b2a99c63857994ece85Jean Chalard 2046b1f500da451de56932a8b2a99c63857994ece85Jean Chalard /** 205923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Delete the last keystroke as a result of hitting backspace. 206923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 207923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void deleteLast() { 20801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok final int size = size(); 209ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (size > 0) { 2109159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard // Note: mTypedWord.length() and mCodes.length differ when there are surrogate pairs 2119159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int stringBuilderLength = mTypedWord.length(); 2129159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard if (stringBuilderLength < size) { 2139159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard throw new RuntimeException( 2149159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard "In WordComposer: mCodes and mTypedWords have non-matching lengths"); 2159159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } 2169159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int lastChar = mTypedWord.codePointBefore(stringBuilderLength); 2179159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard if (Character.isSupplementaryCodePoint(lastChar)) { 2189159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.delete(stringBuilderLength - 2, stringBuilderLength); 2199159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } else { 2209159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.deleteCharAt(stringBuilderLength - 1); 2219159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } 222ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (Character.isUpperCase(lastChar)) mCapsCount--; 223e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard if (Character.isDigit(lastChar)) mDigitsCount--; 22401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 225d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka } 2269159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard // We may have deleted the last one. 22701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok if (0 == size()) { 228ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = false; 229117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 230117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard if (mTrailingSingleQuotesCount > 0) { 231117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard --mTrailingSingleQuotesCount; 232c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } else { 233825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard int i = mTypedWord.length(); 234825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard while (i > 0) { 235825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard i = mTypedWord.offsetByCodePoints(i, -1); 236be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; 237117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard ++mTrailingSingleQuotesCount; 238117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 2398fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 240be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 241923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 242923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 243923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 244923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Returns the word as it was typed, without any correction applied. 245117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard * @return the word that was typed so far. Never returns null. 246923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 2475c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard public String getTypedWord() { 248be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mTypedWord.toString(); 249923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 250923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 251923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 252923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Whether or not the user typed a capital letter as the first letter in the word 253923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @return capitalization preference 254923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 2550b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa public boolean isFirstCharCapitalized() { 2560b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa return mIsFirstCharCapitalized; 257923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 2580b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa 259117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard public int trailingSingleQuotesCount() { 260117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard return mTrailingSingleQuotesCount; 261c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } 262c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard 2630b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa /** 2640b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * Whether or not all of the user typed chars are upper case 2650b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * @return true if all user typed chars are upper case, false otherwise 2660b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa */ 2670b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa public boolean isAllUpperCase() { 268ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard if (size() <= 1) { 269ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED 270ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED; 271ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard } else { 272ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard return mCapsCount == size(); 273ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard } 2741eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard } 2751eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard 2761eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard public boolean wasShiftedNoLock() { 2771eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED 2781eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED; 2790b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa } 2800b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa 281923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 2824a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani * Returns true if more than one character is upper case, otherwise returns false. 2834a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani */ 2844a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani public boolean isMostlyCaps() { 2854a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani return mCapsCount > 1; 2864a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani } 2871c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani 2880fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard /** 289e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard * Returns true if we have digits in the composing word. 290e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard */ 291e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard public boolean hasDigits() { 292e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard return mDigitsCount > 0; 293e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard } 294e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard 295e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard /** 296adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * Saves the caps mode at the start of composing. 297adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * 298adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * WordComposer needs to know about this for several reasons. The first is, we need to know 299adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * after the fact what the reason was, to register the correct form into the user history 300adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * dictionary: if the word was automatically capitalized, we should insert it in all-lower 301adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * case but if it's a manual pressing of shift, then it should be inserted as is. 302adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * Also, batch input needs to know about the current caps mode to display correctly 303adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * capitalized suggestions. 304adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * @param mode the mode at the time of start 3051c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani */ 306adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public void setCapitalizedModeAtStartComposingTime(final int mode) { 307adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard mCapitalizedMode = mode; 3081c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani } 3091c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani 3101c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani /** 3111c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * Returns whether the word was automatically capitalized. 3121c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * @return whether the word was automatically capitalized 3131c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani */ 314adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public boolean wasAutoCapitalized() { 315adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED 316adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED; 3171c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani } 318117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard 319117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard /** 320117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard * Sets the auto-correction for this word. 321117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard */ 322117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard public void setAutoCorrection(final CharSequence correction) { 323be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = correction; 324117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard } 325117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard 326117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard /** 327f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard * @return the auto-correction for this word, or null if none. 328117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard */ 329117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard public CharSequence getAutoCorrectionOrNull() { 330be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mAutoCorrection; 331117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard } 332c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard 3334b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard /** 3344b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard * @return whether we started composing this word by resuming suggestion on an existing string 3354b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard */ 3364b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard public boolean isResumed() { 3374b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard return mIsResumed; 3384b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard } 3394b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard 340267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above. 34166bb563535dbe3672f99f75bd71763a551444867Jean Chalard public LastComposedWord commitWord(final int type, final String committedWord, 342a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard final String separatorString, final CharSequence prevWord) { 3439271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK 3449271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate 3459271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard // the last composed word to ensure this does not happen. 34601ab7c8b59a7f12862fbd95fb252e56719f1757fsatok final int[] primaryKeyCodes = mPrimaryKeyCodes; 34701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mPrimaryKeyCodes = new int[N]; 34801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes, 349a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard mInputPointers, mTypedWord.toString(), committedWord, separatorString, 350c54d558e2e70bdfb2c1078cae7b88440d421dc67satok prevWord); 35171538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka mInputPointers.reset(); 3529271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD 3539271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) { 354449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard lastComposedWord.deactivate(); 355449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard } 356e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard mCapsCount = 0; 357e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard mDigitsCount = 0; 358e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard mIsBatchMode = false; 359be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 3602a37fb9d30848aee42757546e8478cb7a9e45bc6Jean Chalard mTrailingSingleQuotesCount = 0; 361e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard mIsFirstCharCapitalized = false; 36201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 363be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 3644b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = false; 3651f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard return lastComposedWord; 366c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard } 367c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard 3682712f23acbb197af3b125da4cc47108e71b7446dJean Chalard public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) { 36901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes; 37071538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka mInputPointers.set(lastComposedWord.mInputPointers); 371be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 372be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.append(lastComposedWord.mTypedWord); 37301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 374cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard mAutoCorrection = null; // This will be filled by the next call to updateSuggestion. 3754b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = true; 3769e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard } 377d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 378d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka public boolean isBatchMode() { 379d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka return mIsBatchMode; 380d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 381923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 382