WordComposer.java revision 3708787fe91227083d2a1874fa41493d3bc9fe10
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project *
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
7923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0
9923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project *
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
3427dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard    // Storage for all the info about the current input.
3527dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard    private static class CharacterStore {
3627dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        /**
3727dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard         * The list of unicode values for each keystroke (including surrounding keys)
3827dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard         */
3927dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        ArrayList<int[]> mCodes;
4027dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        int[] mXCoordinates;
4127dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        int[] mYCoordinates;
4227dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        StringBuilder mTypedWord;
4327dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        CharacterStore() {
4427dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            final int N = BinaryDictionary.MAX_WORD_LENGTH;
4527dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mCodes = new ArrayList<int[]>(N);
4627dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mTypedWord = new StringBuilder(N);
4727dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mXCoordinates = new int[N];
4827dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mYCoordinates = new int[N];
4927dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        }
5027dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        CharacterStore(final CharacterStore that) {
5127dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mCodes = new ArrayList<int[]>(that.mCodes);
5227dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mTypedWord = new StringBuilder(that.mTypedWord);
5327dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mXCoordinates = Arrays.copyOf(that.mXCoordinates, that.mXCoordinates.length);
5427dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mYCoordinates = Arrays.copyOf(that.mYCoordinates, that.mYCoordinates.length);
5527dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        }
5627dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        void reset() {
5727dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            // For better performance than creating a new character store.
5827dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mCodes.clear();
5927dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mTypedWord.setLength(0);
6027dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        }
6127dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard    }
628fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
63c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    // The currently typing word. May not be null.
6427dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard    private CharacterStore mCurrentWord;
65c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    // The information being kept for resuming suggestion. May be null if wiped.
66c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    private CharacterStore mWordKeptForSuggestionResuming;
67117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    // An auto-correction for this word out of the dictionary.
68117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    private CharSequence mAutoCorrection;
694a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
704a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    private int mCapsCount;
711c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
721c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    private boolean mAutoCapitalized;
73c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard    // Cache this value for performance
74117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    private int mTrailingSingleQuotesCount;
75c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
76923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
770b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether the user chose to capitalize the first char of the word.
78923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
790b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
80923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
81979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public WordComposer() {
8227dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        mCurrentWord = new CharacterStore();
83c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        mWordKeptForSuggestionResuming = null;
84117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
85117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = null;
86923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
87923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
88f733074aaecdfd6e89cfee2daff8a9c1233b60f1satok    public WordComposer(WordComposer source) {
899fbfd5877305ed19a20663630b498b6b3fdae942satok        init(source);
909fbfd5877305ed19a20663630b498b6b3fdae942satok    }
919fbfd5877305ed19a20663630b498b6b3fdae942satok
929fbfd5877305ed19a20663630b498b6b3fdae942satok    public void init(WordComposer source) {
9327dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        mCurrentWord = new CharacterStore(source.mCurrentWord);
94c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        mWordKeptForSuggestionResuming = source.mWordKeptForSuggestionResuming;
95ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mCapsCount = source.mCapsCount;
96ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
97ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mAutoCapitalized = source.mAutoCapitalized;
98117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
99117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = null;
100979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
101979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
103923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Clear out the keys registered so far.
104923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
105923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void reset() {
10627dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        mCurrentWord.reset();
107c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        mWordKeptForSuggestionResuming = null;
1084a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        mCapsCount = 0;
109ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = false;
110117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        mTrailingSingleQuotesCount = 0;
111117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = null;
112923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
113923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
114923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
115923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Number of keystrokes in the composing word.
116923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the number of keystrokes
117923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
118ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    public final int size() {
11927dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        return mCurrentWord.mTypedWord.length();
120923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
121923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
122196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    public final boolean isComposingWord() {
123196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        return size() > 0;
124196d82cdd740580ed79d801483dbc282be85d076Jean Chalard    }
125196d82cdd740580ed79d801483dbc282be85d076Jean Chalard
126923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the codes at a particular position in the word.
128923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param index the position in the word
129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the unicode for the pressed and surrounding keys
130923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
131923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public int[] getCodesAt(int index) {
13227dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        return mCurrentWord.mCodes.get(index);
133923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
134923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1358fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public int[] getXCoordinates() {
13627dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        return mCurrentWord.mXCoordinates;
1378fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1388fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
1398fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public int[] getYCoordinates() {
14027dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        return mCurrentWord.mYCoordinates;
1418fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1428fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
143ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
144ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (index == 0) return Character.isUpperCase(codePoint);
145436a645ea837d36f7e0f81948d343fa6e166f33aTadashi G. Takaoka        return previous && !Character.isUpperCase(codePoint);
146ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka    }
147ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka
148923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
149923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of
150923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * the array containing unicode for adjacent keys, sorted by reducing probability/proximity.
151923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param codes the array of unicode values
152923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
1538fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public void add(int primaryCode, int[] codes, int x, int y) {
154ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        final int newIndex = size();
15527dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        mCurrentWord.mTypedWord.append((char) primaryCode);
156231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        correctPrimaryJuxtapos(primaryCode, codes);
15727dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        mCurrentWord.mCodes.add(codes);
158ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
15927dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mCurrentWord.mXCoordinates[newIndex] = x;
16027dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mCurrentWord.mYCoordinates[newIndex] = y;
1618fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
162ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        mIsFirstCharCapitalized = isFirstCharCapitalized(
163ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka                newIndex, primaryCode, mIsFirstCharCapitalized);
164ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (Character.isUpperCase(primaryCode)) mCapsCount++;
165117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) {
166117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            ++mTrailingSingleQuotesCount;
167117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        } else {
168117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            mTrailingSingleQuotesCount = 0;
169117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        }
170117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = null;
171923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
172923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
173923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
1746b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Internal method to retrieve reasonable proximity info for a character.
1756b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
1763708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka    private void addKeyInfo(final int codePoint, final Keyboard keyboard,
1776b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final KeyDetector keyDetector) {
1786b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        for (final Key key : keyboard.mKeys) {
1796b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            if (key.mCode == codePoint) {
1806b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                final int x = key.mX + key.mWidth / 2;
1816b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                final int y = key.mY + key.mHeight / 2;
1826b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                final int[] codes = keyDetector.newCodeArray();
183e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                keyDetector.getKeyAndNearbyCodes(x, y, codes);
1846b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                add(codePoint, codes, x, y);
1856b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                return;
1866b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            }
1876b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
1886b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        add(codePoint, new int[] { codePoint },
1896b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
1906b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
1916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
1926b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
1936b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Set the currently composing word to the one passed as an argument.
1946b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
1956b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
1963708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka    public void setComposingWord(final CharSequence word, final Keyboard keyboard,
1976b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final KeyDetector keyDetector) {
1986b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        reset();
1996b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final int length = word.length();
2006b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        for (int i = 0; i < length; ++i) {
2016b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            int codePoint = word.charAt(i);
2026b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            addKeyInfo(codePoint, keyboard, keyDetector);
2036b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        }
204117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = null;
2056b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
2066b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
2076b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
2086b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Shortcut for the above method, this will create a new KeyDetector for the passed keyboard.
2096b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
2103708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka    public void setComposingWord(final CharSequence word, final Keyboard keyboard) {
2116b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final KeyDetector keyDetector = new KeyDetector(0);
2126b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        keyDetector.setKeyboard(keyboard, 0, 0);
2136b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        keyDetector.setProximityCorrectionEnabled(true);
2146b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        keyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
2156b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        setComposingWord(word, keyboard, keyDetector);
2166b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
2176b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
2186b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
219231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * Swaps the first and second values in the codes array if the primary code is not the first
220231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * value in the array but the second. This happens when the preferred key is not the key that
221231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * the user released the finger on.
222231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * @param primaryCode the preferred character
223231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * @param codes array of codes based on distance from touch point
224231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     */
2258fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static void correctPrimaryJuxtapos(int primaryCode, int[] codes) {
226231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        if (codes.length < 2) return;
227231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        if (codes[0] > 0 && codes[1] > 0 && codes[0] != primaryCode && codes[1] == primaryCode) {
228231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani            codes[1] = codes[0];
229231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani            codes[0] = primaryCode;
230231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        }
231231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani    }
232231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani
233231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani    /**
234923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Delete the last keystroke as a result of hitting backspace.
235923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
236923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void deleteLast() {
237ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        final int size = size();
238ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (size > 0) {
239ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            final int lastPos = size - 1;
24027dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            char lastChar = mCurrentWord.mTypedWord.charAt(lastPos);
24127dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mCurrentWord.mCodes.remove(lastPos);
24227dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            mCurrentWord.mTypedWord.deleteCharAt(lastPos);
243ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            if (Character.isUpperCase(lastChar)) mCapsCount--;
244d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka        }
245ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka        if (size() == 0) {
246ea843f2a2404f4bc04fda494e475520162cfca27Tadashi G. Takaoka            mIsFirstCharCapitalized = false;
247117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        }
248117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        if (mTrailingSingleQuotesCount > 0) {
249117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            --mTrailingSingleQuotesCount;
250c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard        } else {
25127dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard            for (int i = mCurrentWord.mTypedWord.length() - 1; i >= 0; --i) {
25227dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard                if (Keyboard.CODE_SINGLE_QUOTE != mCurrentWord.mTypedWord.codePointAt(i)) break;
253117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard                ++mTrailingSingleQuotesCount;
254117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard            }
2558fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
256117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = null;
257923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
258923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
259923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
260923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the word as it was typed, without any correction applied.
261117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * @return the word that was typed so far. Never returns null.
262923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
2635c08151c227d98031abe27c3f0a8f43a7126ae9dJean Chalard    public String getTypedWord() {
26427dff63833bf1c8a36eed826746de3c6725d8423Jean Chalard        return mCurrentWord.mTypedWord.toString();
265923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
266923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
267923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
268923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Whether or not the user typed a capital letter as the first letter in the word
269923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return capitalization preference
270923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
2710b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isFirstCharCapitalized() {
2720b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return mIsFirstCharCapitalized;
273923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2740b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
275117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard    public int trailingSingleQuotesCount() {
276117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard        return mTrailingSingleQuotesCount;
277c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard    }
278c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard
2790b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    /**
2800b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether or not all of the user typed chars are upper case
2810b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * @return true if all user typed chars are upper case, false otherwise
2820b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     */
2830b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isAllUpperCase() {
2840b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return (mCapsCount > 0) && (mCapsCount == size());
2850b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    }
2860b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
287923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
2884a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     * Returns true if more than one character is upper case, otherwise returns false.
2894a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     */
2904a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    public boolean isMostlyCaps() {
2914a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        return mCapsCount > 1;
2924a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    }
2931c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
2941c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
2951c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Saves the reason why the word is capitalized - whether it was automatic or
2961c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * due to the user hitting shift in the middle of a sentence.
2971c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @param auto whether it was an automatic capitalization due to start of sentence
2981c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
2991c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    public void setAutoCapitalized(boolean auto) {
3001c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        mAutoCapitalized = auto;
3011c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
3021c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
3031c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
3041c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Returns whether the word was automatically capitalized.
3051c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @return whether the word was automatically capitalized
3061c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
3071c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    public boolean isAutoCapitalized() {
3081c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        return mAutoCapitalized;
3091c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
310117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
311117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
312117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * Sets the auto-correction for this word.
313117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
314117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    public void setAutoCorrection(final CharSequence correction) {
315117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = correction;
316117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
317117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
318117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
319117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     * Remove any auto-correction that may have been set.
320117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
321117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    public void deleteAutoCorrection() {
322117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        mAutoCorrection = null;
323117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
324117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard
325117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    /**
326f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard     * @return the auto-correction for this word, or null if none.
327117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard     */
328117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    public CharSequence getAutoCorrectionOrNull() {
329117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        return mAutoCorrection;
330117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard    }
331c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
332c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    // TODO: pass the information about what was committed and how. Was it an auto-correction?
333c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    // Was it a completion? Was is what the user typed?
334c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    public void onCommitWord() {
335c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        mWordKeptForSuggestionResuming = mCurrentWord;
336c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        // TODO: improve performance by swapping buffers instead of creating a new object.
337c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        mCurrentWord = new CharacterStore();
338c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
339c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
340c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    public boolean hasWordKeptForSuggestionResuming() {
341c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        return null != mWordKeptForSuggestionResuming;
342c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
343c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard
344c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    public void resumeSuggestionOnKeptWord() {
345c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        mCurrentWord = mWordKeptForSuggestionResuming;
346c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard        mWordKeptForSuggestionResuming = null;
347c73c26790fa9dcd836a918774d6efa39a05c0152Jean Chalard    }
348923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
349