WordComposer.java revision 0b4ae1f578e768eec4ada90aeb81d11acb10eb2e
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
19923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport java.util.ArrayList;
20923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
21923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
22923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * A place to store the currently composing word with information such as adjacent key codes as well
23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpublic class WordComposer {
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * The list of unicode values for each keystroke (including surrounding keys)
27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
28d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka    private final ArrayList<int[]> mCodes;
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
30923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
31923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * The word chosen from the candidate list, until it is committed.
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private String mPreferredWord;
34923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
35d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka    private final StringBuilder mTypedWord;
364a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
374a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    private int mCapsCount;
381c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
391c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    private boolean mAutoCapitalized;
40923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
41923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
420b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether the user chose to capitalize the first char of the word.
43923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
440b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    private boolean mIsFirstCharCapitalized;
45923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
46979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public WordComposer() {
47923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mCodes = new ArrayList<int[]>(12);
48923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mTypedWord = new StringBuilder(20);
49923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
51979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    WordComposer(WordComposer copy) {
52525141a402ac9a3fb3495cb069ad25b9ba1fc970satok        mCodes = new ArrayList<int[]>(copy.mCodes);
53979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mPreferredWord = copy.mPreferredWord;
54979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mTypedWord = new StringBuilder(copy.mTypedWord);
55979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mCapsCount = copy.mCapsCount;
56979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mAutoCapitalized = copy.mAutoCapitalized;
570b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        mIsFirstCharCapitalized = copy.mIsFirstCharCapitalized;
58979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
59979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
60923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
61923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Clear out the keys registered so far.
62923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
63923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void reset() {
64923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mCodes.clear();
650b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        mIsFirstCharCapitalized = false;
66923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPreferredWord = null;
67923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mTypedWord.setLength(0);
684a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        mCapsCount = 0;
69923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
70923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
71923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
72923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Number of keystrokes in the composing word.
73923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the number of keystrokes
74923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
75923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public int size() {
76923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return mCodes.size();
77923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
78923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
79923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
80923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the codes at a particular position in the word.
81923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param index the position in the word
82923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the unicode for the pressed and surrounding keys
83923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
84923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public int[] getCodesAt(int index) {
85923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return mCodes.get(index);
86923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
87923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
88923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
89923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of
90923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * the array containing unicode for adjacent keys, sorted by reducing probability/proximity.
91923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param codes the array of unicode values
92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
93923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void add(int primaryCode, int[] codes) {
94923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mTypedWord.append((char) primaryCode);
95231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        correctPrimaryJuxtapos(primaryCode, codes);
96923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mCodes.add(codes);
974a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        if (Character.isUpperCase((char) primaryCode)) mCapsCount++;
98923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
99923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
100923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
101231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * Swaps the first and second values in the codes array if the primary code is not the first
102231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * value in the array but the second. This happens when the preferred key is not the key that
103231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * the user released the finger on.
104231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * @param primaryCode the preferred character
105231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     * @param codes array of codes based on distance from touch point
106231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani     */
107231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani    private void correctPrimaryJuxtapos(int primaryCode, int[] codes) {
108231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        if (codes.length < 2) return;
109231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        if (codes[0] > 0 && codes[1] > 0 && codes[0] != primaryCode && codes[1] == primaryCode) {
110231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani            codes[1] = codes[0];
111231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani            codes[0] = primaryCode;
112231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani        }
113231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani    }
114231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani
115231cacd08075e88a2bcdf25f025206de524e880bAmith Yamasani    /**
116923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Delete the last keystroke as a result of hitting backspace.
117923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
118923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void deleteLast() {
119d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka        final int codesSize = mCodes.size();
120d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka        if (codesSize > 0) {
121d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka            mCodes.remove(codesSize - 1);
122d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka            final int lastPos = mTypedWord.length() - 1;
123d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka            char last = mTypedWord.charAt(lastPos);
124d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka            mTypedWord.deleteCharAt(lastPos);
125d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka            if (Character.isUpperCase(last)) mCapsCount--;
126d1a8e3088bb6267a31e3351d304796d1507e3af6Tadashi G. Takaoka        }
127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
128923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
130923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Returns the word as it was typed, without any correction applied.
131923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the word that was typed so far
132923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
133923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public CharSequence getTypedWord() {
134923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        int wordSize = mCodes.size();
135923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (wordSize == 0) {
136923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return null;
137923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
138923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return mTypedWord;
139923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
140923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1410b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public void setFirstCharCapitalized(boolean capitalized) {
1420b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        mIsFirstCharCapitalized = capitalized;
143923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
144923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
145923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
146923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Whether or not the user typed a capital letter as the first letter in the word
147923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return capitalization preference
148923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
1490b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isFirstCharCapitalized() {
1500b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return mIsFirstCharCapitalized;
151923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1520b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
1530b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    /**
1540b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * Whether or not all of the user typed chars are upper case
1550b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     * @return true if all user typed chars are upper case, false otherwise
1560b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa     */
1570b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    public boolean isAllUpperCase() {
1580b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa        return (mCapsCount > 0) && (mCapsCount == size());
1590b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa    }
1600b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa
161923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
162923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Stores the user's selected word, before it is actually committed to the text field.
163923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param preferred
164923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
165923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void setPreferredWord(String preferred) {
166923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPreferredWord = preferred;
167923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
168923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
169923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
170923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * Return the word chosen by the user, or the typed word if no other word was chosen.
171923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @return the preferred word
172923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
173923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public CharSequence getPreferredWord() {
174923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return mPreferredWord != null ? mPreferredWord : getTypedWord();
175923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1764a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
1774a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    /**
1784a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     * Returns true if more than one character is upper case, otherwise returns false.
1794a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani     */
1804a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    public boolean isMostlyCaps() {
1814a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        return mCapsCount > 1;
1824a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani    }
1831c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
1841c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
1851c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Saves the reason why the word is capitalized - whether it was automatic or
1861c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * due to the user hitting shift in the middle of a sentence.
1871c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @param auto whether it was an automatic capitalization due to start of sentence
1881c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
1891c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    public void setAutoCapitalized(boolean auto) {
1901c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        mAutoCapitalized = auto;
1911c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
1921c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
1931c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    /**
1941c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * Returns whether the word was automatically capitalized.
1951c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     * @return whether the word was automatically capitalized
1961c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani     */
1971c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    public boolean isAutoCapitalized() {
1981c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        return mAutoCapitalized;
1991c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
200923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
201