diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
7 files changed, 92 insertions, 56 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index dfc8c8e9d..a9df1ce12 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -55,6 +55,8 @@ public class BinaryDictionary extends Dictionary { public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING = new Flag(R.bool.config_require_umlaut_processing, 0x1); + public static final Flag FLAG_REQUIRES_FRENCH_LIGATURES_PROCESSING = + new Flag(R.bool.config_require_ligatures_processing, 0x4); // FULL_EDIT_DISTANCE is a flag that forces the dictionary to use full words // when computing edit distance, instead of the default behavior of stopping @@ -77,6 +79,7 @@ public class BinaryDictionary extends Dictionary { // actual value will be read from the configuration/extra value at run time for // the configuration at dictionary creation time. FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING, + FLAG_REQUIRES_FRENCH_LIGATURES_PROCESSING, }; private int mFlags = 0; @@ -202,9 +205,11 @@ public class BinaryDictionary extends Dictionary { Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE); for (int i = 0; i < codesSize; i++) { - int[] alternatives = codes.getCodesAt(i); - System.arraycopy(alternatives, 0, mInputCodes, i * MAX_PROXIMITY_CHARS_SIZE, - Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE)); + final int[] alternatives = codes.getCodesAt(i); + if (alternatives == null || alternatives.length < 1) { + continue; + } + mInputCodes[i] = alternatives[0]; } Arrays.fill(outputChars, (char) 0); Arrays.fill(scores, 0); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 48fb79809..7903820f7 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -197,7 +197,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private InputMethodManagerCompatWrapper mImm; private Resources mResources; private SharedPreferences mPrefs; - private final KeyboardSwitcher mKeyboardSwitcher; + /* package for tests */ final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeSwitcher mSubtypeSwitcher; private VoiceProxy mVoiceProxy; private boolean mShouldSwitchToLastSubtype = true; diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 9f23f174f..cabf68099 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -20,8 +20,6 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; -import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService; -import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; import java.util.ArrayList; import java.util.Arrays; @@ -127,12 +125,8 @@ public class WordComposer { final int[] codes; final int keyX; final int keyY; - if (x == KeyboardActionListener.SPELL_CHECKER_COORDINATE - || y == KeyboardActionListener.SPELL_CHECKER_COORDINATE) { - // only used for tests in InputLogicTests - addKeyForSpellChecker(primaryCode, AndroidSpellCheckerService.SCRIPT_LATIN); - return; - } else if (x == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE + if (null == keyDetector + || x == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE || y == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE || x == KeyboardActionListener.NOT_A_TOUCH_COORDINATE || y == KeyboardActionListener.NOT_A_TOUCH_COORDINATE) { @@ -149,27 +143,6 @@ public class WordComposer { add(primaryCode, codes, keyX, keyY); } - // TODO: remove this function - public void addKeyForSpellChecker(int primaryCode, int script) { - final int[] proximities; - final int proximityIndex = - SpellCheckerProximityInfo.getIndexOfCodeForScript(primaryCode, script); - if (-1 == proximityIndex) { - proximities = new int[] { primaryCode }; - } else { - // TODO: an initial examination seems to reveal this is actually used - // read-only. It should be possible to compute the arrays statically once - // and skip doing a copy each time here. - proximities = Arrays.copyOfRange( - SpellCheckerProximityInfo.getProximityForScript(script), - proximityIndex, - proximityIndex + SpellCheckerProximityInfo.ROW_SIZE); - } - add(primaryCode, proximities, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE); - } - /** * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of * the array containing unicode for adjacent keys, sorted by reducing probability/proximity. @@ -197,8 +170,7 @@ public class WordComposer { /** * Internal method to retrieve reasonable proximity info for a character. */ - private void addKeyInfo(final int codePoint, final Keyboard keyboard, - final KeyDetector keyDetector) { + private void addKeyInfo(final int codePoint, final Keyboard keyboard) { for (final Key key : keyboard.mKeys) { if (key.mCode == codePoint) { final int x = key.mX + key.mWidth / 2; @@ -216,27 +188,16 @@ public class WordComposer { * Set the currently composing word to the one passed as an argument. * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity. */ - private void setComposingWord(final CharSequence word, final Keyboard keyboard, - final KeyDetector keyDetector) { + public void setComposingWord(final CharSequence word, final Keyboard keyboard) { reset(); final int length = word.length(); for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) { int codePoint = Character.codePointAt(word, i); - addKeyInfo(codePoint, keyboard, keyDetector); + addKeyInfo(codePoint, keyboard); } } /** - * Shortcut for the above method, this will create a new KeyDetector for the passed keyboard. - */ - public void setComposingWord(final CharSequence word, final Keyboard keyboard) { - final KeyDetector keyDetector = new KeyDetector(0); - keyDetector.setKeyboard(keyboard, 0, 0); - keyDetector.setProximityCorrectionEnabled(true); - setComposingWord(word, keyboard, keyDetector); - } - - /** * Delete the last keystroke as a result of hitting backspace. */ public void deleteLast() { diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 9dc294edf..64fcd7f1a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -64,6 +64,19 @@ public class FusionDictionary implements Iterable<Word> { mWord = word; mFrequency = frequency; } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[] { mWord, mFrequency }); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof WeightedString)) return false; + WeightedString w = (WeightedString)o; + return mWord.equals(w.mWord) && mFrequency == w.mFrequency; + } } /** diff --git a/java/src/com/android/inputmethod/latin/makedict/Word.java b/java/src/com/android/inputmethod/latin/makedict/Word.java index c2c01e1f8..4e0ab1049 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Word.java +++ b/java/src/com/android/inputmethod/latin/makedict/Word.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import java.util.ArrayList; +import java.util.Arrays; /** * Utility class for a word with a frequency. @@ -32,6 +33,8 @@ public class Word implements Comparable<Word> { final ArrayList<WeightedString> mShortcutTargets; final ArrayList<WeightedString> mBigrams; + private int mHashCode = 0; + public Word(final String word, final int frequency, final ArrayList<WeightedString> shortcutTargets, final ArrayList<WeightedString> bigrams, final boolean isShortcutOnly) { @@ -42,6 +45,16 @@ public class Word implements Comparable<Word> { mIsShortcutOnly = isShortcutOnly; } + private static int computeHashCode(Word word) { + return Arrays.hashCode(new Object[] { + word.mWord, + word.mFrequency, + word.mIsShortcutOnly, + word.mShortcutTargets.hashCode(), + word.mBigrams.hashCode() + }); + } + /** * Three-way comparison. * @@ -63,10 +76,20 @@ public class Word implements Comparable<Word> { */ @Override public boolean equals(Object o) { + if (o == this) return true; if (!(o instanceof Word)) return false; Word w = (Word)o; return mFrequency == w.mFrequency && mWord.equals(w.mWord) + && mIsShortcutOnly == w.mIsShortcutOnly && mShortcutTargets.equals(w.mShortcutTargets) && mBigrams.equals(w.mBigrams); } + + @Override + public int hashCode() { + if (mHashCode == 0) { + mHashCode = computeHashCode(this); + } + return mHashCode; + } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 35a5c0f52..973a448ee 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -570,7 +570,11 @@ public class AndroidSpellCheckerService extends SpellCheckerService final WordComposer composer = new WordComposer(); final int length = text.length(); for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { - composer.addKeyForSpellChecker(text.codePointAt(i), mScript); + final int codePoint = text.codePointAt(i); + // The getXYForCodePointAndScript method returns (Y << 16) + X + final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript( + codePoint, mScript); + composer.add(codePoint, xy & 0xFFFF, xy >> 16, null); } final int capitalizeType = getCapitalizationType(text); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index db3544987..7627700dd 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -30,11 +30,16 @@ public class SpellCheckerProximityInfo { // as the size of the passed array afterwards so they can't be different. final public static int ROW_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE; + // The number of keys in a row of the grid used by the spell checker. + final public static int PROXIMITY_GRID_WIDTH = 11; + // The number of rows in the grid used by the spell checker. + final public static int PROXIMITY_GRID_HEIGHT = 3; + // Helper methods final protected static void buildProximityIndices(final int[] proximity, final TreeMap<Integer, Integer> indices) { for (int i = 0; i < proximity.length; i += ROW_SIZE) { - if (NUL != proximity[i]) indices.put(proximity[i], i); + if (NUL != proximity[i]) indices.put(proximity[i], i / ROW_SIZE); } } final protected static int computeIndex(final int characterCode, @@ -64,6 +69,9 @@ public class SpellCheckerProximityInfo { // to English, many spelling errors consist of the last vowel of the word being wrong // because in English vowels tend to merge with each other in pronunciation. final static int[] PROXIMITY = { + // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter, + // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's. + // The number of rows must be exactly PROXIMITY_GRID_HEIGHT. 'q', 'w', 's', 'a', 'z', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'w', 'q', 'a', 's', 'd', 'e', 'x', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'e', 'w', 's', 'd', 'f', 'r', 'a', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL, @@ -76,9 +84,10 @@ public class SpellCheckerProximityInfo { 'p', 'o', 'l', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + // Proximity for row 2. See comment above about size. 'a', 'z', 'x', 's', 'w', 'q', 'e', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL, 's', 'q', 'a', 'z', 'x', 'c', 'd', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'd', 'w', 's', 'x', 'c', 'v', 'f', 'r', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, + 'd', 'w', 's', 'x', 'c', 'v', 'f', 'r', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'f', 'e', 'd', 'c', 'v', 'b', 'g', 't', 'r', NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'g', 'r', 'f', 'v', 'b', 'n', 'h', 'y', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'h', 't', 'g', 'b', 'n', 'm', 'j', 'u', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, @@ -88,6 +97,7 @@ public class SpellCheckerProximityInfo { NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + // Proximity for row 3. See comment above about size. 'z', 'a', 's', 'd', 'x', 't', 'g', 'h', 'j', 'u', 'q', 'e', NUL, NUL, NUL, NUL, 'x', 'z', 'a', 's', 'd', 'c', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'c', 'x', 's', 'd', 'f', 'v', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, @@ -109,9 +119,12 @@ public class SpellCheckerProximityInfo { private static class Cyrillic { final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>(); + // TODO: The following table is solely based on the keyboard layout. Consult with Russian + // speakers on commonly misspelled words/letters. final static int[] PROXIMITY = { - // TODO: This table is solely based on the keyboard layout. Consult with Russian - // speakers on commonly misspelled words/letters. + // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter, + // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's. + // The number of rows must be exactly PROXIMITY_GRID_HEIGHT. 'й', 'ц', 'ф', 'ы', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'ц', 'й', 'ф', 'ы', 'в', 'у', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'у', 'ц', 'ы', 'в', 'а', 'к', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, @@ -124,6 +137,7 @@ public class SpellCheckerProximityInfo { 'з', 'щ', 'д', 'ж', 'э', 'х', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'х', 'з', 'ж', 'э', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + // Proximity for row 2. See comment above about size. 'ф', 'й', 'ц', 'ы', 'я', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'ы', 'й', 'ц', 'у', 'ф', 'в', 'я', 'ч', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'в', 'ц', 'у', 'к', 'ы', 'а', 'я', 'ч', 'с', NUL, NUL, NUL, NUL, NUL, NUL, NUL, @@ -136,6 +150,7 @@ public class SpellCheckerProximityInfo { 'ж', 'щ', 'з', 'х', 'д', 'э', 'б', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'э', 'з', 'х', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + // Proximity for row 3. See comment above about size. 'я', 'ф', 'ы', 'в', 'ч', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'ч', 'ы', 'в', 'а', 'я', 'с', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'с', 'в', 'а', 'п', 'ч', 'м', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, @@ -166,14 +181,29 @@ public class SpellCheckerProximityInfo { throw new RuntimeException("Wrong script supplied: " + script); } } - public static int getIndexOfCodeForScript(final int characterCode, final int script) { + + private static int getIndexOfCodeForScript(final int codePoint, final int script) { switch (script) { case AndroidSpellCheckerService.SCRIPT_LATIN: - return Latin.getIndexOf(characterCode); + return Latin.getIndexOf(codePoint); case AndroidSpellCheckerService.SCRIPT_CYRILLIC: - return Cyrillic.getIndexOf(characterCode); + return Cyrillic.getIndexOf(codePoint); default: throw new RuntimeException("Wrong script supplied: " + script); } } + + // Returns (Y << 16) + X to avoid creating a temporary object. This is okay because + // X and Y are limited to PROXIMITY_GRID_WIDTH resp. PROXIMITY_GRID_HEIGHT which is very + // inferior to 1 << 16 + public static int getXYForCodePointAndScript(final int codePoint, final int script) { + final int index = getIndexOfCodeForScript(codePoint, script); + final int y = index / PROXIMITY_GRID_WIDTH; + final int x = index % PROXIMITY_GRID_WIDTH; + if (y > PROXIMITY_GRID_HEIGHT) { + // Safety check, should be entirely useless + throw new RuntimeException("Wrong y coordinate in spell checker proximity"); + } + return (y << 16) + x; + } } |