WordComposer.java revision cbed462d192d0c5af9614f5f997b2768f3d0eb56
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/* 2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project 30fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 70fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 90fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard * 10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License. 15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin; 18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 197ede642df417c0f732573f639970b138f0bee18cJean Chalardimport com.android.inputmethod.event.CombinerChain; 20f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport com.android.inputmethod.event.Event; 21f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport com.android.inputmethod.latin.utils.CollectionUtils; 22d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalardimport com.android.inputmethod.latin.utils.CoordinateUtils; 235fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalardimport com.android.inputmethod.latin.utils.StringUtils; 24887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaoka 25f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport java.util.ArrayList; 26c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalardimport java.util.Arrays; 27f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalardimport java.util.Collections; 28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 30923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * A place to store the currently composing word with information such as adjacent key codes as well 31923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 32a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class WordComposer { 33ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataoka private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; 340e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard private static final boolean DBG = LatinImeLogger.sDBG; 358fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 36adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_OFF = 0; 37adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits 38adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard // aren't used anywhere in the code 39adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1; 40adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3; 41adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_AUTO_SHIFTED = 0x5; 42adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7; 43adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard 447ede642df417c0f732573f639970b138f0bee18cJean Chalard private CombinerChain mCombinerChain; 457ede642df417c0f732573f639970b138f0bee18cJean Chalard 465fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // An array of code points representing the characters typed so far. 475fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // The array is limited to MAX_WORD_LENGTH code points, but mTypedWord extends past that 485fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // and mCodePointSize can go past that. If mCodePointSize is greater than MAX_WORD_LENGTH, 495fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // this just does not contain the associated code points past MAX_WORD_LENGTH. 5001ab7c8b59a7f12862fbd95fb252e56719f1757fsatok private int[] mPrimaryKeyCodes; 51f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard // The list of events that served to compose this string. 52f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard private final ArrayList<Event> mEvents; 5396b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH); 545fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // This is the typed word, as a StringBuilder. This has the same contents as mPrimaryKeyCodes 555fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // but under a StringBuilder representation for ease of use, depending on what is more useful 565fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // at any given time. However this is not limited in size, while mPrimaryKeyCodes is limited 575fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // to MAX_WORD_LENGTH code points. 586b4ce58fc6216b9befd0567b56522ee32f2471a2Tadashi G. Takaoka private final StringBuilder mTypedWord; 592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa // The previous word (before the composing word). Used as context for suggestions. May be null 602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa // after resetting and before starting a new composing word, or when there is no context like 617cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard // at the start of text for example. It can also be set to null externally when the user 627cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard // enters a separator that does not let bigrams across, like a period or a comma. 637cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard private String mPreviousWordForSuggestion; 64bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private String mAutoCorrection; 654b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard private boolean mIsResumed; 66d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka private boolean mIsBatchMode; 67d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user 68d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard // gestures a word, is displeased with the results and hits backspace, then gestures again. 69d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard // At the very least we should avoid re-suggesting the same thing, and to do that we memorize 70d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard // the rejected suggestion in this variable. 71d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard // TODO: this should be done in a comprehensive way by the User History feature instead of 72d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard // as an ad-hockery here. 73d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard private String mRejectedBatchModeSuggestion; 744a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani 75be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard // Cache these values for performance 764a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani private int mCapsCount; 77e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard private int mDigitsCount; 78adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard private int mCapitalizedMode; 79117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard private int mTrailingSingleQuotesCount; 805fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // This is the number of code points entered so far. This is not limited to MAX_WORD_LENGTH. 815fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // In general, this contains the size of mPrimaryKeyCodes, except when this is greater than 825fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // MAX_WORD_LENGTH in which case mPrimaryKeyCodes only contain the first MAX_WORD_LENGTH 835fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // code points. 8401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok private int mCodePointSize; 856a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard private int mCursorPositionWithinWord; 86c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard 87923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 880b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * Whether the user chose to capitalize the first char of the word. 89923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 900b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa private boolean mIsFirstCharCapitalized; 91923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 92979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public WordComposer() { 937ede642df417c0f732573f639970b138f0bee18cJean Chalard mCombinerChain = new CombinerChain(); 9496b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa mPrimaryKeyCodes = new int[MAX_WORD_LENGTH]; 95f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard mEvents = CollectionUtils.newArrayList(); 9696b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa mTypedWord = new StringBuilder(MAX_WORD_LENGTH); 97be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 98117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 994b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = false; 100d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = false; 1016a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = 0; 102d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard mRejectedBatchModeSuggestion = null; 1037cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard mPreviousWordForSuggestion = null; 10401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 105923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 106923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 107bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public WordComposer(final WordComposer source) { 1087ede642df417c0f732573f639970b138f0bee18cJean Chalard mCombinerChain = source.mCombinerChain; 10901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length); 110f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard mEvents = new ArrayList<Event>(source.mEvents); 111be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord = new StringBuilder(source.mTypedWord); 11271538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka mInputPointers.copy(source.mInputPointers); 113ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mCapsCount = source.mCapsCount; 114e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard mDigitsCount = source.mDigitsCount; 115ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; 116adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard mCapitalizedMode = source.mCapitalizedMode; 117117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount; 1184b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = source.mIsResumed; 119d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = source.mIsBatchMode; 1206a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = source.mCursorPositionWithinWord; 121d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion; 1227cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard mPreviousWordForSuggestion = source.mPreviousWordForSuggestion; 12301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 124979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 125979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 126923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Clear out the keys registered so far. 128923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void reset() { 130cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard mCombinerChain.reset(); 131be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 132f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard mEvents.clear(); 133be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 1344a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani mCapsCount = 0; 135e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard mDigitsCount = 0; 136ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = false; 137117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 1384b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = false; 139d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = false; 1406a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = 0; 141d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard mRejectedBatchModeSuggestion = null; 1427cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard mPreviousWordForSuggestion = null; 14301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 14401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok } 14501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok 146e55c23e4b0b8d9d66349a3b275d0fa1540d7450aKen Wakasa private final void refreshSize() { 14701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length()); 148923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 149923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 150923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 151923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Number of keystrokes in the composing word. 152923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @return the number of keystrokes 153923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 154ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka public final int size() { 15501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok return mCodePointSize; 156923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 157923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 158196d82cdd740580ed79d801483dbc282be85d076Jean Chalard public final boolean isComposingWord() { 15901ab7c8b59a7f12862fbd95fb252e56719f1757fsatok return size() > 0; 160196d82cdd740580ed79d801483dbc282be85d076Jean Chalard } 161196d82cdd740580ed79d801483dbc282be85d076Jean Chalard 1629611b281e18ac71d825ff5bc771a111423772cb3satok // TODO: make sure that the index should not exceed MAX_WORD_LENGTH 16301ab7c8b59a7f12862fbd95fb252e56719f1757fsatok public int getCodeAt(int index) { 16496b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa if (index >= MAX_WORD_LENGTH) { 1659611b281e18ac71d825ff5bc771a111423772cb3satok return -1; 1669611b281e18ac71d825ff5bc771a111423772cb3satok } 16701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok return mPrimaryKeyCodes[index]; 168923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 169923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 1700e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard public int getCodeBeforeCursor() { 1710e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard if (mCursorPositionWithinWord < 1 || mCursorPositionWithinWord > mPrimaryKeyCodes.length) { 1720e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard return Constants.NOT_A_CODE; 1730e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard } 1740e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard return mPrimaryKeyCodes[mCursorPositionWithinWord - 1]; 1750e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard } 1760e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard 17771538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka public InputPointers getInputPointers() { 17871538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka return mInputPointers; 1798fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 1808fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 181bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private static boolean isFirstCharCapitalized(final int index, final int codePoint, 182bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final boolean previous) { 183ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (index == 0) return Character.isUpperCase(codePoint); 184436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka return previous && !Character.isUpperCase(codePoint); 185ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka } 186ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka 187923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 188f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard * Add a new event for a key stroke, with the pressed key's code point with the touch point 189f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard * coordinates. 190923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 191f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard public void add(final Event event) { 192f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard final int primaryCode = event.mCodePoint; 193f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard final int keyX = event.mX; 194f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard final int keyY = event.mY; 19501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok final int newIndex = size(); 196f2bb15b0ab212e1ef45be2d2ea6610cfa9c9f15cJean Chalard mCombinerChain.processEvent(mEvents, event); 197835965a75e7dad3026911b6615efa502905f3eabJean Chalard // TODO: remove mTypedWord and compute it dynamically when necessary. We also need to 198835965a75e7dad3026911b6615efa502905f3eabJean Chalard // make the views of the composing word a SpannableString. 199835965a75e7dad3026911b6615efa502905f3eabJean Chalard mTypedWord.replace(0, mTypedWord.length(), 200835965a75e7dad3026911b6615efa502905f3eabJean Chalard mCombinerChain.getComposingWordWithCombiningFeedback().toString()); 201f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard mEvents.add(event); 20201ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 2036a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = mCodePointSize; 20496b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa if (newIndex < MAX_WORD_LENGTH) { 205240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE 20667094f5bdece00994f70c6f1fa9a6ff7b8f3c3c1satok ? Character.toLowerCase(primaryCode) : primaryCode; 207eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // In the batch input mode, the {@code mInputPointers} holds batch input points and 208eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // shouldn't be overridden by the "typed key" coordinates 209eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (See {@link #setBatchInputWord}). 210eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang if (!mIsBatchMode) { 211eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // TODO: Set correct pointer id and time 212ad78058a93492d4f114c6a6eb56177be9231a9ebTadashi G. Takaoka mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0); 213eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 2148fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 215ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = isFirstCharCapitalized( 216ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka newIndex, primaryCode, mIsFirstCharCapitalized); 217ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (Character.isUpperCase(primaryCode)) mCapsCount++; 218e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard if (Character.isDigit(primaryCode)) mDigitsCount++; 219240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka if (Constants.CODE_SINGLE_QUOTE == primaryCode) { 220117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard ++mTrailingSingleQuotesCount; 221117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } else { 222117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = 0; 223117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 224be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 225923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 226923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 2276a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard public void setCursorPositionWithinWord(final int posWithinWord) { 2286a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = posWithinWord; 229f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard // TODO: compute where that puts us inside the events 2306a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard } 2316a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard 2320e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard public boolean isCursorFrontOrMiddleOfComposingWord() { 2330e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard if (DBG && mCursorPositionWithinWord > mCodePointSize) { 2340e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord 2350e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard + "in a word of size " + mCodePointSize); 2360e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard } 2370e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard return mCursorPositionWithinWord != mCodePointSize; 2386a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard } 2396a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard 240f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard /** 241f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard * When the cursor is moved by the user, we need to update its position. 242f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard * If it falls inside the currently composing word, we don't reset the composition, and 243f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard * only update the cursor position. 244f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard * 245f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard * @param expectedMoveAmount How many java chars to move the cursor. Negative values move 246f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard * the cursor backward, positive values move the cursor forward. 247f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard * @return true if the cursor is still inside the composing word, false otherwise. 248f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard */ 249f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) { 250cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard // TODO: should uncommit the composing feedback 251cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard mCombinerChain.reset(); 252f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard int actualMoveAmountWithinWord = 0; 253f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard int cursorPos = mCursorPositionWithinWord; 2545fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard final int[] codePoints; 2555fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard if (mCodePointSize >= MAX_WORD_LENGTH) { 2565fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // If we have more than MAX_WORD_LENGTH characters, we don't have everything inside 2575fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // mPrimaryKeyCodes. This should be rare enough that we can afford to just compute 2585fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard // the array on the fly when this happens. 2595fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard codePoints = StringUtils.toCodePointArray(mTypedWord.toString()); 2605fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard } else { 2615fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard codePoints = mPrimaryKeyCodes; 2625fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard } 263f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard if (expectedMoveAmount >= 0) { 264f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard // Moving the cursor forward for the expected amount or until the end of the word has 265f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard // been reached, whichever comes first. 266f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard while (actualMoveAmountWithinWord < expectedMoveAmount && cursorPos < mCodePointSize) { 2675fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard actualMoveAmountWithinWord += Character.charCount(codePoints[cursorPos]); 268f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard ++cursorPos; 269f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard } 270f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard } else { 271f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard // Moving the cursor backward for the expected amount or until the start of the word 272f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard // has been reached, whichever comes first. 273f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard while (actualMoveAmountWithinWord > expectedMoveAmount && cursorPos > 0) { 274f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard --cursorPos; 2755fa2202e36a84bd5808fa10ca25c9179acb1b173Jean Chalard actualMoveAmountWithinWord -= Character.charCount(codePoints[cursorPos]); 276f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard } 277f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard } 278f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard // If the actual and expected amounts differ, we crossed the start or the end of the word 279f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard // so the result would not be inside the composing word. 280f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard if (actualMoveAmountWithinWord != expectedMoveAmount) return false; 281f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard mCursorPositionWithinWord = cursorPos; 282f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard return true; 283f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard } 284f0af452ce261590b5978a1bb679ce27b71f9dc70Jean Chalard 285bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public void setBatchInputPointers(final InputPointers batchPointers) { 286eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang mInputPointers.set(batchPointers); 287d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka mIsBatchMode = true; 288d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 289d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 290bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public void setBatchInputWord(final String word) { 291eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang reset(); 292eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang mIsBatchMode = true; 293eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int length = word.length(); 294eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) { 295eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int codePoint = Character.codePointAt(word, i); 296eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // We don't want to override the batch input points that are held in mInputPointers 297eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (See {@link #add(int,int,int)}). 298f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard add(Event.createEventForCodePointFromUnknownSource(codePoint)); 299eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 300eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 301eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang 302923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 3036b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * Set the currently composing word to the one passed as an argument. 3046b1f500da451de56932a8b2a99c63857994ece85Jean Chalard * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity. 305d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard * @param codePoints the code points to set as the composing word. 306d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard * @param coordinates the x, y coordinates of the key in the CoordinateUtils format 3072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @param previousWord the previous word, to use as context for suggestions. Can be null if 3082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * the context is nil (typically, at start of text). 3096b1f500da451de56932a8b2a99c63857994ece85Jean Chalard */ 310d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard public void setComposingWord(final int[] codePoints, final int[] coordinates, 3111284e556f4ec998c6f3ea80905d690cbb4c9dc82Jean Chalard final CharSequence previousWord) { 3126b1f500da451de56932a8b2a99c63857994ece85Jean Chalard reset(); 313d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard final int length = codePoints.length; 314d1f463eacfaac31a999f7eb1ecaa1668ed3038d4Jean Chalard for (int i = 0; i < length; ++i) { 315f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard add(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i], 316f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard CoordinateUtils.xFromArray(coordinates, i), 317f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard CoordinateUtils.yFromArray(coordinates, i))); 3186b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 3194b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = true; 3204752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); 3216b1f500da451de56932a8b2a99c63857994ece85Jean Chalard } 3226b1f500da451de56932a8b2a99c63857994ece85Jean Chalard 3236b1f500da451de56932a8b2a99c63857994ece85Jean Chalard /** 324923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Delete the last keystroke as a result of hitting backspace. 325923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 326923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void deleteLast() { 32701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok final int size = size(); 328ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (size > 0) { 3299159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard // Note: mTypedWord.length() and mCodes.length differ when there are surrogate pairs 3309159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int stringBuilderLength = mTypedWord.length(); 3319159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard if (stringBuilderLength < size) { 3329159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard throw new RuntimeException( 3339159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard "In WordComposer: mCodes and mTypedWords have non-matching lengths"); 3349159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } 3359159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard final int lastChar = mTypedWord.codePointBefore(stringBuilderLength); 336f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard // TODO: with events and composition, this is absolutely not necessarily true. 337f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard mEvents.remove(mEvents.size() - 1); 3389159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard if (Character.isSupplementaryCodePoint(lastChar)) { 3399159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.delete(stringBuilderLength - 2, stringBuilderLength); 3409159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } else { 3419159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard mTypedWord.deleteCharAt(stringBuilderLength - 1); 3429159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard } 343ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka if (Character.isUpperCase(lastChar)) mCapsCount--; 344e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard if (Character.isDigit(lastChar)) mDigitsCount--; 34501ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 346d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka } 3479159b9953d857de83ae2f90a121fcd259f5ee01dJean Chalard // We may have deleted the last one. 34801ab7c8b59a7f12862fbd95fb252e56719f1757fsatok if (0 == size()) { 349ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka mIsFirstCharCapitalized = false; 350117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 351117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard if (mTrailingSingleQuotesCount > 0) { 352117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard --mTrailingSingleQuotesCount; 353c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } else { 354825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard int i = mTypedWord.length(); 355825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard while (i > 0) { 356825e2bbd910cce3055a4ca808d3744bc0b2ceddaJean Chalard i = mTypedWord.offsetByCodePoints(i, -1); 357240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka if (Constants.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; 358117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard ++mTrailingSingleQuotesCount; 359117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 3608fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 3616a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = mCodePointSize; 362be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 363923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 364923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 365923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 366923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Returns the word as it was typed, without any correction applied. 367117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard * @return the word that was typed so far. Never returns null. 368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 3695c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard public String getTypedWord() { 370be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mTypedWord.toString(); 371923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 372923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 3737cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard public String getPreviousWordForSuggestion() { 3747cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard return mPreviousWordForSuggestion; 3752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 3762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa 377923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 378923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Whether or not the user typed a capital letter as the first letter in the word 379923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @return capitalization preference 380923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 3810b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa public boolean isFirstCharCapitalized() { 3820b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa return mIsFirstCharCapitalized; 383923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 3840b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa 385117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard public int trailingSingleQuotesCount() { 386117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard return mTrailingSingleQuotesCount; 387c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } 388c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard 3890b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa /** 3900b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * Whether or not all of the user typed chars are upper case 3910b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa * @return true if all user typed chars are upper case, false otherwise 3920b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa */ 3930b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa public boolean isAllUpperCase() { 394ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard if (size() <= 1) { 395ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED 396ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED; 397ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard } else { 398ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard return mCapsCount == size(); 399ad0642cf258ca9b123f74ca0ae8bf970792908f1Jean Chalard } 4001eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard } 4011eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard 4021eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard public boolean wasShiftedNoLock() { 4031eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED 4041eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED; 4050b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa } 4060b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa 407923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 4084a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani * Returns true if more than one character is upper case, otherwise returns false. 4094a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani */ 4104a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani public boolean isMostlyCaps() { 4114a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani return mCapsCount > 1; 4124a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani } 4131c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani 4140fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard /** 415e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard * Returns true if we have digits in the composing word. 416e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard */ 417e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard public boolean hasDigits() { 418e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard return mDigitsCount > 0; 419e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard } 420e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard 421e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard /** 4222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * Saves the caps mode and the previous word at the start of composing. 423adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * 4242fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * WordComposer needs to know about the caps mode for several reasons. The first is, we need 4252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * to know after the fact what the reason was, to register the correct form into the user 4262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * history dictionary: if the word was automatically capitalized, we should insert it in 4272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * all-lower case but if it's a manual pressing of shift, then it should be inserted as is. 428adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * Also, batch input needs to know about the current caps mode to display correctly 429adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * capitalized suggestions. 430adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard * @param mode the mode at the time of start 4312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @param previousWord the previous word as context for suggestions. May be null if none. 4321c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani */ 4332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode, 4344752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard final CharSequence previousWord) { 435adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard mCapitalizedMode = mode; 4364752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); 4371c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani } 4381c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani 4391c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani /** 4401c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * Returns whether the word was automatically capitalized. 4411c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani * @return whether the word was automatically capitalized 4421c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani */ 443adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard public boolean wasAutoCapitalized() { 444adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED 445adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED; 4461c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani } 447117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard 448117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard /** 449117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard * Sets the auto-correction for this word. 450117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard */ 451bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public void setAutoCorrection(final String correction) { 452be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = correction; 453117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard } 454117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard 455117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard /** 456f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard * @return the auto-correction for this word, or null if none. 457117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard */ 458bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public String getAutoCorrectionOrNull() { 459be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard return mAutoCorrection; 460117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard } 461c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard 4624b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard /** 4634b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard * @return whether we started composing this word by resuming suggestion on an existing string 4644b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard */ 4654b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard public boolean isResumed() { 4664b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard return mIsResumed; 4674b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard } 4684b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard 469267563d1bb4d8091293fbd8774f0f95ef59f03c4Jean Chalard // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above. 4704752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard // committedWord should contain suggestion spans if applicable. 4714752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard public LastComposedWord commitWord(final int type, final CharSequence committedWord, 472bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String separatorString, final String prevWord) { 4739271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK 4749271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate 4759271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard // the last composed word to ensure this does not happen. 47601ab7c8b59a7f12862fbd95fb252e56719f1757fsatok final int[] primaryKeyCodes = mPrimaryKeyCodes; 47796b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa mPrimaryKeyCodes = new int[MAX_WORD_LENGTH]; 478f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes, mEvents, 479a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard mInputPointers, mTypedWord.toString(), committedWord, separatorString, 480b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard prevWord, mCapitalizedMode); 48171538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka mInputPointers.reset(); 4829271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD 4839271b770e81350e232c351f76f9f7a2ec23dff5fJean Chalard && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) { 484449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard lastComposedWord.deactivate(); 485449415c72f437f523a49a9ccfcde8a3c0f583a18Jean Chalard } 486e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard mCapsCount = 0; 487e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard mDigitsCount = 0; 488e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard mIsBatchMode = false; 4894752b68f5a62ede099677bdea0514ba1d5082606Jean Chalard mPreviousWordForSuggestion = committedWord.toString(); 490be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 491cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard mCombinerChain.reset(); 492f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard mEvents.clear(); 4938914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard mCodePointSize = 0; 4942a37fb9d30848aee42757546e8478cb7a9e45bc6Jean Chalard mTrailingSingleQuotesCount = 0; 495e9808694fecbf7be776cd5cf8ec0333e158286b1Jean Chalard mIsFirstCharCapitalized = false; 4968914555776a4d3dfd6afc4926a69169ca1c82a0eJean Chalard mCapitalizedMode = CAPS_MODE_OFF; 49701ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 498be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mAutoCorrection = null; 4996a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = 0; 5004b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = false; 501d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard mRejectedBatchModeSuggestion = null; 5021f8fc62ccb5018716457dc309ab11ad3e1506ad1Jean Chalard return lastComposedWord; 503c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard } 504fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard 5057cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard // Call this when the recorded previous word should be discarded. This is typically called 5067cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard // when the user inputs a separator that's not whitespace (including the case of the 5077cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard // double-space-to-period feature). 5087cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard public void discardPreviousWordForSuggestion() { 5097cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard mPreviousWordForSuggestion = null; 510fae1ba767ca177510adc08b363987f67bbf40d90Jean Chalard } 511c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard 5122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord, 5132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final String previousWord) { 51401ab7c8b59a7f12862fbd95fb252e56719f1757fsatok mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes; 515f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard mEvents.clear(); 516f8accd8839d291f10b218e64aa6b8eb154c92c4cJean Chalard Collections.copy(mEvents, lastComposedWord.mEvents); 51771538b08e4e08d556f700ad344562ca2c7b74d82Satoshi Kataoka mInputPointers.set(lastComposedWord.mInputPointers); 518be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.setLength(0); 519cbed462d192d0c5af9614f5f997b2768f3d0eb56Jean Chalard mCombinerChain.reset(); 520be79227dc99421ff7be62224c51c553b3fa73777Jean Chalard mTypedWord.append(lastComposedWord.mTypedWord); 52101ab7c8b59a7f12862fbd95fb252e56719f1757fsatok refreshSize(); 522b6b7f5e39e9ea1bf9a05203c536327a6be7e7214Jean Chalard mCapitalizedMode = lastComposedWord.mCapitalizedMode; 523cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard mAutoCorrection = null; // This will be filled by the next call to updateSuggestion. 5246a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard mCursorPositionWithinWord = mCodePointSize; 525d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard mRejectedBatchModeSuggestion = null; 5264b5b46bb66bf74ef5edd65c55e186b02f3c56e5dJean Chalard mIsResumed = true; 5277cd7cf73f4ce6f0e577d6382eb0fc25f60dc63e1Jean Chalard mPreviousWordForSuggestion = previousWord; 5289e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard } 529d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 530d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka public boolean isBatchMode() { 531d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka return mIsBatchMode; 532d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 533d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard 534d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard public void setRejectedBatchModeSuggestion(final String rejectedSuggestion) { 535d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard mRejectedBatchModeSuggestion = rejectedSuggestion; 536d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard } 537d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard 538d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard public String getRejectedBatchModeSuggestion() { 539d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard return mRejectedBatchModeSuggestion; 540d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard } 541923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 542