diff options
Diffstat (limited to 'java/src/com/android/inputmethod/event')
5 files changed, 279 insertions, 332 deletions
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java index 2d2731f21..d77ece8e6 100644 --- a/java/src/com/android/inputmethod/event/CombinerChain.java +++ b/java/src/com/android/inputmethod/event/CombinerChain.java @@ -19,10 +19,9 @@ package com.android.inputmethod.event; import android.text.SpannableStringBuilder; import android.text.TextUtils; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.ArrayList; -import java.util.HashMap; import javax.annotation.Nonnull; @@ -45,13 +44,6 @@ public class CombinerChain { private SpannableStringBuilder mStateFeedback; private final ArrayList<Combiner> mCombiners; - private static final HashMap<String, Class<? extends Combiner>> IMPLEMENTED_COMBINERS = - new HashMap<>(); - static { - IMPLEMENTED_COMBINERS.put("MyanmarReordering", MyanmarReordering.class); - } - private static final String COMBINER_SPEC_SEPARATOR = ";"; - /** * Create an combiner chain. * @@ -61,15 +53,11 @@ public class CombinerChain { * cursor: we'll start after this. * * @param initialText The text that has already been combined so far. - * @param combinerList A list of combiners to be applied in order. */ - public CombinerChain(final String initialText, final Combiner... combinerList) { + public CombinerChain(final String initialText) { mCombiners = new ArrayList<>(); // The dead key combiner is always active, and always first mCombiners.add(new DeadKeyCombiner()); - for (final Combiner combiner : combinerList) { - mCombiners.add(combiner); - } mCombinedText = new StringBuilder(initialText); mStateFeedback = new SpannableStringBuilder(); } @@ -97,7 +85,8 @@ public class CombinerChain { * new event. However it may never be null. */ @Nonnull - public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { + public Event processEvent(final ArrayList<Event> previousEvents, + @Nonnull final Event newEvent) { final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents); Event event = newEvent; for (final Combiner combiner : mCombiners) { @@ -145,30 +134,4 @@ public class CombinerChain { final SpannableStringBuilder s = new SpannableStringBuilder(mCombinedText); return s.append(mStateFeedback); } - - public static Combiner[] createCombiners(final String spec) { - if (TextUtils.isEmpty(spec)) { - return new Combiner[0]; - } - final String[] combinerDescriptors = spec.split(COMBINER_SPEC_SEPARATOR); - final Combiner[] combiners = new Combiner[combinerDescriptors.length]; - int i = 0; - for (final String combinerDescriptor : combinerDescriptors) { - final Class<? extends Combiner> combinerClass = - IMPLEMENTED_COMBINERS.get(combinerDescriptor); - if (null == combinerClass) { - throw new RuntimeException("Unknown combiner descriptor: " + combinerDescriptor); - } - try { - combiners[i++] = combinerClass.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException("Unable to instantiate combiner: " + combinerDescriptor, - e); - } catch (IllegalAccessException e) { - throw new RuntimeException("Unable to instantiate combiner: " + combinerDescriptor, - e); - } - } - return combiners; - } } diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java index 4f3f4d25f..1a28bb1d5 100644 --- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java +++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java @@ -17,10 +17,11 @@ package com.android.inputmethod.event; import android.text.TextUtils; -import android.view.KeyCharacterMap; +import android.util.SparseIntArray; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; +import java.text.Normalizer; import java.util.ArrayList; import javax.annotation.Nonnull; @@ -29,9 +30,208 @@ import javax.annotation.Nonnull; * A combiner that handles dead keys. */ public class DeadKeyCombiner implements Combiner { + + private static class Data { + // This class data taken from KeyCharacterMap.java. + + /* Characters used to display placeholders for dead keys. */ + private static final int ACCENT_ACUTE = '\u00B4'; + private static final int ACCENT_BREVE = '\u02D8'; + private static final int ACCENT_CARON = '\u02C7'; + private static final int ACCENT_CEDILLA = '\u00B8'; + private static final int ACCENT_CIRCUMFLEX = '\u02C6'; + private static final int ACCENT_COMMA_ABOVE = '\u1FBD'; + private static final int ACCENT_COMMA_ABOVE_RIGHT = '\u02BC'; + private static final int ACCENT_DOT_ABOVE = '\u02D9'; + private static final int ACCENT_DOT_BELOW = Constants.CODE_PERIOD; // approximate + private static final int ACCENT_DOUBLE_ACUTE = '\u02DD'; + private static final int ACCENT_GRAVE = '\u02CB'; + private static final int ACCENT_HOOK_ABOVE = '\u02C0'; + private static final int ACCENT_HORN = Constants.CODE_SINGLE_QUOTE; // approximate + private static final int ACCENT_MACRON = '\u00AF'; + private static final int ACCENT_MACRON_BELOW = '\u02CD'; + private static final int ACCENT_OGONEK = '\u02DB'; + private static final int ACCENT_REVERSED_COMMA_ABOVE = '\u02BD'; + private static final int ACCENT_RING_ABOVE = '\u02DA'; + private static final int ACCENT_STROKE = Constants.CODE_DASH; // approximate + private static final int ACCENT_TILDE = '\u02DC'; + private static final int ACCENT_TURNED_COMMA_ABOVE = '\u02BB'; + private static final int ACCENT_UMLAUT = '\u00A8'; + private static final int ACCENT_VERTICAL_LINE_ABOVE = '\u02C8'; + private static final int ACCENT_VERTICAL_LINE_BELOW = '\u02CC'; + + /* Legacy dead key display characters used in previous versions of the API (before L) + * We still support these characters by mapping them to their non-legacy version. */ + private static final int ACCENT_GRAVE_LEGACY = Constants.CODE_GRAVE_ACCENT; + private static final int ACCENT_CIRCUMFLEX_LEGACY = Constants.CODE_CIRCUMFLEX_ACCENT; + private static final int ACCENT_TILDE_LEGACY = Constants.CODE_TILDE; + + /** + * Maps Unicode combining diacritical to display-form dead key. + */ + static final SparseIntArray sCombiningToAccent = new SparseIntArray(); + static final SparseIntArray sAccentToCombining = new SparseIntArray(); + static { + // U+0300: COMBINING GRAVE ACCENT + addCombining('\u0300', ACCENT_GRAVE); + // U+0301: COMBINING ACUTE ACCENT + addCombining('\u0301', ACCENT_ACUTE); + // U+0302: COMBINING CIRCUMFLEX ACCENT + addCombining('\u0302', ACCENT_CIRCUMFLEX); + // U+0303: COMBINING TILDE + addCombining('\u0303', ACCENT_TILDE); + // U+0304: COMBINING MACRON + addCombining('\u0304', ACCENT_MACRON); + // U+0306: COMBINING BREVE + addCombining('\u0306', ACCENT_BREVE); + // U+0307: COMBINING DOT ABOVE + addCombining('\u0307', ACCENT_DOT_ABOVE); + // U+0308: COMBINING DIAERESIS + addCombining('\u0308', ACCENT_UMLAUT); + // U+0309: COMBINING HOOK ABOVE + addCombining('\u0309', ACCENT_HOOK_ABOVE); + // U+030A: COMBINING RING ABOVE + addCombining('\u030A', ACCENT_RING_ABOVE); + // U+030B: COMBINING DOUBLE ACUTE ACCENT + addCombining('\u030B', ACCENT_DOUBLE_ACUTE); + // U+030C: COMBINING CARON + addCombining('\u030C', ACCENT_CARON); + // U+030D: COMBINING VERTICAL LINE ABOVE + addCombining('\u030D', ACCENT_VERTICAL_LINE_ABOVE); + // U+030E: COMBINING DOUBLE VERTICAL LINE ABOVE + //addCombining('\u030E', ACCENT_DOUBLE_VERTICAL_LINE_ABOVE); + // U+030F: COMBINING DOUBLE GRAVE ACCENT + //addCombining('\u030F', ACCENT_DOUBLE_GRAVE); + // U+0310: COMBINING CANDRABINDU + //addCombining('\u0310', ACCENT_CANDRABINDU); + // U+0311: COMBINING INVERTED BREVE + //addCombining('\u0311', ACCENT_INVERTED_BREVE); + // U+0312: COMBINING TURNED COMMA ABOVE + addCombining('\u0312', ACCENT_TURNED_COMMA_ABOVE); + // U+0313: COMBINING COMMA ABOVE + addCombining('\u0313', ACCENT_COMMA_ABOVE); + // U+0314: COMBINING REVERSED COMMA ABOVE + addCombining('\u0314', ACCENT_REVERSED_COMMA_ABOVE); + // U+0315: COMBINING COMMA ABOVE RIGHT + addCombining('\u0315', ACCENT_COMMA_ABOVE_RIGHT); + // U+031B: COMBINING HORN + addCombining('\u031B', ACCENT_HORN); + // U+0323: COMBINING DOT BELOW + addCombining('\u0323', ACCENT_DOT_BELOW); + // U+0326: COMBINING COMMA BELOW + //addCombining('\u0326', ACCENT_COMMA_BELOW); + // U+0327: COMBINING CEDILLA + addCombining('\u0327', ACCENT_CEDILLA); + // U+0328: COMBINING OGONEK + addCombining('\u0328', ACCENT_OGONEK); + // U+0329: COMBINING VERTICAL LINE BELOW + addCombining('\u0329', ACCENT_VERTICAL_LINE_BELOW); + // U+0331: COMBINING MACRON BELOW + addCombining('\u0331', ACCENT_MACRON_BELOW); + // U+0335: COMBINING SHORT STROKE OVERLAY + addCombining('\u0335', ACCENT_STROKE); + // U+0342: COMBINING GREEK PERISPOMENI + //addCombining('\u0342', ACCENT_PERISPOMENI); + // U+0344: COMBINING GREEK DIALYTIKA TONOS + //addCombining('\u0344', ACCENT_DIALYTIKA_TONOS); + // U+0345: COMBINING GREEK YPOGEGRAMMENI + //addCombining('\u0345', ACCENT_YPOGEGRAMMENI); + + // One-way mappings to equivalent preferred accents. + // U+0340: COMBINING GRAVE TONE MARK + sCombiningToAccent.append('\u0340', ACCENT_GRAVE); + // U+0341: COMBINING ACUTE TONE MARK + sCombiningToAccent.append('\u0341', ACCENT_ACUTE); + // U+0343: COMBINING GREEK KORONIS + sCombiningToAccent.append('\u0343', ACCENT_COMMA_ABOVE); + + // One-way legacy mappings to preserve compatibility with older applications. + // U+0300: COMBINING GRAVE ACCENT + sAccentToCombining.append(ACCENT_GRAVE_LEGACY, '\u0300'); + // U+0302: COMBINING CIRCUMFLEX ACCENT + sAccentToCombining.append(ACCENT_CIRCUMFLEX_LEGACY, '\u0302'); + // U+0303: COMBINING TILDE + sAccentToCombining.append(ACCENT_TILDE_LEGACY, '\u0303'); + } + + private static void addCombining(int combining, int accent) { + sCombiningToAccent.append(combining, accent); + sAccentToCombining.append(accent, combining); + } + + // Caution! This may only contain chars, not supplementary code points. It's unlikely + // it will ever need to, but if it does we'll have to change this + private static final SparseIntArray sNonstandardDeadCombinations = new SparseIntArray(); + static { + // Non-standard decompositions. + // Stroke modifier for Finnish multilingual keyboard and others. + // U+0110: LATIN CAPITAL LETTER D WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'D', '\u0110'); + // U+01E4: LATIN CAPITAL LETTER G WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'G', '\u01e4'); + // U+0126: LATIN CAPITAL LETTER H WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'H', '\u0126'); + // U+0197: LATIN CAPITAL LETTER I WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'I', '\u0197'); + // U+0141: LATIN CAPITAL LETTER L WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'L', '\u0141'); + // U+00D8: LATIN CAPITAL LETTER O WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'O', '\u00d8'); + // U+0166: LATIN CAPITAL LETTER T WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'T', '\u0166'); + // U+0111: LATIN SMALL LETTER D WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'd', '\u0111'); + // U+01E5: LATIN SMALL LETTER G WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'g', '\u01e5'); + // U+0127: LATIN SMALL LETTER H WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'h', '\u0127'); + // U+0268: LATIN SMALL LETTER I WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'i', '\u0268'); + // U+0142: LATIN SMALL LETTER L WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'l', '\u0142'); + // U+00F8: LATIN SMALL LETTER O WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 'o', '\u00f8'); + // U+0167: LATIN SMALL LETTER T WITH STROKE + addNonStandardDeadCombination(ACCENT_STROKE, 't', '\u0167'); + } + + private static void addNonStandardDeadCombination(final int deadCodePoint, + final int spacingCodePoint, final int result) { + final int combination = (deadCodePoint << 16) | spacingCodePoint; + sNonstandardDeadCombinations.put(combination, result); + } + + public static final int NOT_A_CHAR = 0; + public static final int BITS_TO_SHIFT_DEAD_CODE_POINT_FOR_NON_STANDARD_COMBINATION = 16; + // Get a non-standard combination + public static char getNonstandardCombination(final int deadCodePoint, + final int spacingCodePoint) { + final int combination = spacingCodePoint | + (deadCodePoint << BITS_TO_SHIFT_DEAD_CODE_POINT_FOR_NON_STANDARD_COMBINATION); + return (char)sNonstandardDeadCombinations.get(combination, NOT_A_CHAR); + } + } + // TODO: make this a list of events instead final StringBuilder mDeadSequence = new StringBuilder(); + @Nonnull + private static Event createEventChainFromSequence(final @Nonnull CharSequence text, + @Nonnull final Event originalEvent) { + int index = text.length(); + if (index <= 0) { + return originalEvent; + } + Event lastEvent = null; + do { + final int codePoint = Character.codePointBefore(text, index); + lastEvent = Event.createHardwareKeypressEvent(codePoint, + originalEvent.mKeyCode, lastEvent, false /* isKeyRepeat */); + index -= Character.charCount(codePoint); + } while (index > 0); + return lastEvent; + } + @Override @Nonnull public Event processEvent(final ArrayList<Event> previousEvents, final Event event) { @@ -46,32 +246,49 @@ public class DeadKeyCombiner implements Combiner { // no dead keys at all in the current input, so this combiner has nothing to do and // simply returns the event as is. The majority of events will go through this path. return event; - } else { - // TODO: Allow combining for several dead chars rather than only the first one. - // The framework doesn't know how to do this now. - final int deadCodePoint = mDeadSequence.codePointAt(0); + } + if (Character.isWhitespace(event.mCodePoint) + || event.mCodePoint == mDeadSequence.codePointBefore(mDeadSequence.length())) { + // When whitespace or twice the same dead key, we should output the dead sequence as is. + final Event resultEvent = createEventChainFromSequence(mDeadSequence.toString(), + event); mDeadSequence.setLength(0); - final int resultingCodePoint = - KeyCharacterMap.getDeadChar(deadCodePoint, event.mCodePoint); - if (0 == resultingCodePoint) { - // We can't combine both characters. We need to commit the dead key as a separate - // character, and the next char too unless it's a space (because as a special case, - // dead key + space should result in only the dead key being committed - that's - // how dead keys work). - // If the event is a space, we should commit the dead char alone, but if it's - // not, we need to commit both. - // TODO: this is not necessarily triggered by hardware key events, so it's not - // a good idea to masquerade as one. This should be typed as a software - // composite event or something. - return Event.createHardwareKeypressEvent(deadCodePoint, event.mKeyCode, - Constants.CODE_SPACE == event.mCodePoint ? null : event /* next */, - false /* isKeyRepeat */); + return resultEvent; + } + if (event.isFunctionalKeyEvent()) { + if (Constants.CODE_DELETE == event.mKeyCode) { + // Remove the last code point + final int trimIndex = mDeadSequence.length() - Character.charCount( + mDeadSequence.codePointBefore(mDeadSequence.length())); + mDeadSequence.setLength(trimIndex); + return Event.createConsumedEvent(event); + } + return event; + } + if (event.isDead()) { + mDeadSequence.appendCodePoint(event.mCodePoint); + return Event.createConsumedEvent(event); + } + // Combine normally. + final StringBuilder sb = new StringBuilder(); + sb.appendCodePoint(event.mCodePoint); + int codePointIndex = 0; + while (codePointIndex < mDeadSequence.length()) { + final int deadCodePoint = mDeadSequence.codePointAt(codePointIndex); + final char replacementSpacingChar = + Data.getNonstandardCombination(deadCodePoint, event.mCodePoint); + if (Data.NOT_A_CHAR != replacementSpacingChar) { + sb.setCharAt(0, replacementSpacingChar); } else { - // We could combine the characters. - return Event.createHardwareKeypressEvent(resultingCodePoint, event.mKeyCode, - null /* next */, false /* isKeyRepeat */); + final int combining = Data.sAccentToCombining.get(deadCodePoint); + sb.appendCodePoint(0 == combining ? deadCodePoint : combining); } + codePointIndex += Character.isSupplementaryCodePoint(deadCodePoint) ? 2 : 1; } + final String normalizedString = Normalizer.normalize(sb, Normalizer.Form.NFC); + final Event resultEvent = createEventChainFromSequence(normalizedString, event); + mDeadSequence.setLength(0); + return resultEvent; } @Override diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java index ef5b04747..e3b1afc53 100644 --- a/java/src/com/android/inputmethod/event/Event.java +++ b/java/src/com/android/inputmethod/event/Event.java @@ -16,9 +16,12 @@ package com.android.inputmethod.event; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; + +import javax.annotation.Nonnull; /** * Class representing a generic input event as handled by Latin IME. @@ -55,6 +58,8 @@ public class Event { final public static int EVENT_TYPE_SUGGESTION_PICKED = 5; // An event corresponding to a string generated by some software process. final public static int EVENT_TYPE_SOFTWARE_GENERATED_STRING = 6; + // An event corresponding to a cursor move + final public static int EVENT_TYPE_CURSOR_MOVE = 7; // 0 is a valid code point, so we use -1 here. final public static int NOT_A_CODE_POINT = -1; @@ -133,12 +138,14 @@ public class Event { } } + @Nonnull public static Event createSoftwareKeypressEvent(final int codePoint, final int keyCode, final int x, final int y, final boolean isKeyRepeat) { return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, x, y, null /* suggestedWordInfo */, isKeyRepeat ? FLAG_REPEAT : FLAG_NONE, null); } + @Nonnull public static Event createHardwareKeypressEvent(final int codePoint, final int keyCode, final Event next, final boolean isKeyRepeat) { return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, @@ -147,6 +154,8 @@ public class Event { } // This creates an input event for a dead character. @see {@link #FLAG_DEAD} + @ExternallyReferenced + @Nonnull public static Event createDeadEvent(final int codePoint, final int keyCode, final Event next) { // TODO: add an argument or something if we ever create a software layout with dead keys. return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, @@ -161,6 +170,7 @@ public class Event { * @param codePoint the code point. * @return an event for this code point. */ + @Nonnull public static Event createEventForCodePointFromUnknownSource(final int codePoint) { // TODO: should we have a different type of event for this? After all, it's not a key press. return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE, @@ -176,6 +186,7 @@ public class Event { * @param y the Y coordinate. * @return an event for this code point and coordinates. */ + @Nonnull public static Event createEventForCodePointFromAlreadyTypedText(final int codePoint, final int x, final int y) { // TODO: should we have a different type of event for this? After all, it's not a key press. @@ -187,6 +198,7 @@ public class Event { * Creates an input event representing the manual pick of a suggestion. * @return an event for this suggestion pick. */ + @Nonnull public static Event createSuggestionPickedEvent(final SuggestedWordInfo suggestedWordInfo) { return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, NOT_A_CODE_POINT, NOT_A_KEY_CODE, @@ -202,6 +214,7 @@ public class Event { * @param keyCode the key code, or NOT_A_KEYCODE if not applicable. * @return an event for this text. */ + @Nonnull public static Event createSoftwareTextEvent(final CharSequence text, final int keyCode) { return new Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, @@ -212,6 +225,7 @@ public class Event { * Creates an input event representing the manual pick of a punctuation suggestion. * @return an event for this suggestion pick. */ + @Nonnull public static Event createPunctuationSuggestionPickedEvent( final SuggestedWordInfo suggestedWordInfo) { final int primaryCode = suggestedWordInfo.mWord.charAt(0); @@ -222,10 +236,23 @@ public class Event { } /** + * Creates an input event representing moving the cursor. The relative move amount is stored + * in mX. + * @param moveAmount the relative move amount. + * @return an event for this cursor move. + */ + @Nonnull + public static Event createCursorMovedEvent(final int moveAmount) { + return new Event(EVENT_TYPE_CURSOR_MOVE, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, + moveAmount, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null); + } + + /** * Creates an event identical to the passed event, but that has already been consumed. * @param source the event to copy the properties of. * @return an identical event marked as consumed. */ + @Nonnull public static Event createConsumedEvent(final Event source) { // A consumed event should not input any text at all, so we pass the empty string as text. return new Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, @@ -233,6 +260,7 @@ public class Event { source.mNextEvent); } + @Nonnull public static Event createNotHandledEvent() { return new Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, @@ -277,6 +305,7 @@ public class Event { case EVENT_TYPE_MODE_KEY: case EVENT_TYPE_NOT_HANDLED: case EVENT_TYPE_TOGGLE: + case EVENT_TYPE_CURSOR_MOVE: return ""; case EVENT_TYPE_INPUT_KEYPRESS: return StringUtils.newSingleCodePointString(mCodePoint); diff --git a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java index c61f45efa..3a4097d7f 100644 --- a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java +++ b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java @@ -19,7 +19,7 @@ package com.android.inputmethod.event; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; /** * A hardware event decoder for a hardware qwerty-ish keyboard. @@ -67,10 +67,9 @@ public class HardwareKeyboardEventDecoder implements HardwareEventDecoder { if (keyEvent.isShiftPressed()) { return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, Constants.CODE_SHIFT_ENTER, null /* next */, isKeyRepeat); - } else { - return Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode, - null /* next */, isKeyRepeat); } + return Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode, + null /* next */, isKeyRepeat); } // If not Enter, then this is just a regular keypress event for a normal character // that can be committed right away, taking into account the current state. diff --git a/java/src/com/android/inputmethod/event/MyanmarReordering.java b/java/src/com/android/inputmethod/event/MyanmarReordering.java deleted file mode 100644 index dcd06c899..000000000 --- a/java/src/com/android/inputmethod/event/MyanmarReordering.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.event; - -import com.android.inputmethod.latin.Constants; - -import java.util.ArrayList; -import java.util.Arrays; - -import javax.annotation.Nonnull; - -/** - * A combiner that reorders input for Myanmar. - */ -public class MyanmarReordering implements Combiner { - // U+1031 MYANMAR VOWEL SIGN E - private final static int VOWEL_E = 0x1031; // Code point for vowel E that we need to reorder - // U+200C ZERO WIDTH NON-JOINER - // U+200B ZERO WIDTH SPACE - private final static int ZERO_WIDTH_NON_JOINER = 0x200B; // should be 0x200C - - private final ArrayList<Event> mCurrentEvents = new ArrayList<>(); - - // List of consonants : - // U+1000 MYANMAR LETTER KA - // U+1001 MYANMAR LETTER KHA - // U+1002 MYANMAR LETTER GA - // U+1003 MYANMAR LETTER GHA - // U+1004 MYANMAR LETTER NGA - // U+1005 MYANMAR LETTER CA - // U+1006 MYANMAR LETTER CHA - // U+1007 MYANMAR LETTER JA - // U+1008 MYANMAR LETTER JHA - // U+1009 MYANMAR LETTER NYA - // U+100A MYANMAR LETTER NNYA - // U+100B MYANMAR LETTER TTA - // U+100C MYANMAR LETTER TTHA - // U+100D MYANMAR LETTER DDA - // U+100E MYANMAR LETTER DDHA - // U+100F MYANMAR LETTER NNA - // U+1010 MYANMAR LETTER TA - // U+1011 MYANMAR LETTER THA - // U+1012 MYANMAR LETTER DA - // U+1013 MYANMAR LETTER DHA - // U+1014 MYANMAR LETTER NA - // U+1015 MYANMAR LETTER PA - // U+1016 MYANMAR LETTER PHA - // U+1017 MYANMAR LETTER BA - // U+1018 MYANMAR LETTER BHA - // U+1019 MYANMAR LETTER MA - // U+101A MYANMAR LETTER YA - // U+101B MYANMAR LETTER RA - // U+101C MYANMAR LETTER LA - // U+101D MYANMAR LETTER WA - // U+101E MYANMAR LETTER SA - // U+101F MYANMAR LETTER HA - // U+1020 MYANMAR LETTER LLA - // U+103F MYANMAR LETTER GREAT SA - private static boolean isConsonant(final int codePoint) { - return (codePoint >= 0x1000 && codePoint <= 0x1020) || 0x103F == codePoint; - } - - // List of medials : - // U+103B MYANMAR CONSONANT SIGN MEDIAL YA - // U+103C MYANMAR CONSONANT SIGN MEDIAL RA - // U+103D MYANMAR CONSONANT SIGN MEDIAL WA - // U+103E MYANMAR CONSONANT SIGN MEDIAL HA - // U+105E MYANMAR CONSONANT SIGN MON MEDIAL NA - // U+105F MYANMAR CONSONANT SIGN MON MEDIAL MA - // U+1060 MYANMAR CONSONANT SIGN MON MEDIAL LA - // U+1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA - private static int[] MEDIAL_LIST = { 0x103B, 0x103C, 0x103D, 0x103E, - 0x105E, 0x105F, 0x1060, 0x1082}; - private static boolean isMedial(final int codePoint) { - return Arrays.binarySearch(MEDIAL_LIST, codePoint) >= 0; - } - - private static boolean isConsonantOrMedial(final int codePoint) { - return isConsonant(codePoint) || isMedial(codePoint); - } - - private Event getLastEvent() { - final int size = mCurrentEvents.size(); - if (size <= 0) { - return null; - } - return mCurrentEvents.get(size - 1); - } - - private CharSequence getCharSequence() { - final StringBuilder s = new StringBuilder(); - for (final Event e : mCurrentEvents) { - s.appendCodePoint(e.mCodePoint); - } - return s; - } - - /** - * Clears the currently combining stream of events and returns the resulting software text - * event corresponding to the stream. Optionally adds a new event to the cleared stream. - * @param newEvent the new event to add to the stream. null if none. - * @return the resulting software text event. Never null. - */ - private Event clearAndGetResultingEvent(final Event newEvent) { - final CharSequence combinedText; - if (mCurrentEvents.size() > 0) { - combinedText = getCharSequence(); - mCurrentEvents.clear(); - } else { - combinedText = null; - } - if (null != newEvent) { - mCurrentEvents.add(newEvent); - } - return null == combinedText ? Event.createConsumedEvent(newEvent) - : Event.createSoftwareTextEvent(combinedText, Event.NOT_A_KEY_CODE); - } - - @Override - @Nonnull - public Event processEvent(ArrayList<Event> previousEvents, Event newEvent) { - final int codePoint = newEvent.mCodePoint; - if (VOWEL_E == codePoint) { - final Event lastEvent = getLastEvent(); - if (null == lastEvent) { - mCurrentEvents.add(newEvent); - return Event.createConsumedEvent(newEvent); - } else if (isConsonantOrMedial(lastEvent.mCodePoint)) { - final Event resultingEvent = clearAndGetResultingEvent(null); - mCurrentEvents.add(Event.createSoftwareKeypressEvent(ZERO_WIDTH_NON_JOINER, - Event.NOT_A_KEY_CODE, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, - false /* isKeyRepeat */)); - mCurrentEvents.add(newEvent); - return resultingEvent; - } else { // VOWEL_E == lastCodePoint. But if that was anything else this is correct too. - return clearAndGetResultingEvent(newEvent); - } - } if (isConsonant(codePoint)) { - final Event lastEvent = getLastEvent(); - if (null == lastEvent) { - mCurrentEvents.add(newEvent); - return Event.createConsumedEvent(newEvent); - } else if (VOWEL_E == lastEvent.mCodePoint) { - final int eventSize = mCurrentEvents.size(); - if (eventSize >= 2 - && mCurrentEvents.get(eventSize - 2).mCodePoint == ZERO_WIDTH_NON_JOINER) { - // We have a ZWJN before a vowel E. We need to remove the ZWNJ and then - // reorder the vowel with respect to the consonant. - mCurrentEvents.remove(eventSize - 1); - mCurrentEvents.remove(eventSize - 2); - mCurrentEvents.add(newEvent); - mCurrentEvents.add(lastEvent); - return Event.createConsumedEvent(newEvent); - } - // If there is already a consonant, then we are starting a new syllable. - for (int i = eventSize - 2; i >= 0; --i) { - if (isConsonant(mCurrentEvents.get(i).mCodePoint)) { - return clearAndGetResultingEvent(newEvent); - } - } - // If we come here, we didn't have a consonant so we reorder - mCurrentEvents.remove(eventSize - 1); - mCurrentEvents.add(newEvent); - mCurrentEvents.add(lastEvent); - return Event.createConsumedEvent(newEvent); - } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine - return clearAndGetResultingEvent(newEvent); - } - } else if (isMedial(codePoint)) { - final Event lastEvent = getLastEvent(); - if (null == lastEvent) { - mCurrentEvents.add(newEvent); - return Event.createConsumedEvent(newEvent); - } else if (VOWEL_E == lastEvent.mCodePoint) { - final int eventSize = mCurrentEvents.size(); - // If there is already a consonant, then we are in the middle of a syllable, and we - // need to reorder. - boolean hasConsonant = false; - for (int i = eventSize - 2; i >= 0; --i) { - if (isConsonant(mCurrentEvents.get(i).mCodePoint)) { - hasConsonant = true; - break; - } - } - if (hasConsonant) { - mCurrentEvents.remove(eventSize - 1); - mCurrentEvents.add(newEvent); - mCurrentEvents.add(lastEvent); - return Event.createConsumedEvent(newEvent); - } - // Otherwise, we just commit everything. - return clearAndGetResultingEvent(null); - } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine - return clearAndGetResultingEvent(newEvent); - } - } else if (Constants.CODE_DELETE == newEvent.mKeyCode) { - final Event lastEvent = getLastEvent(); - final int eventSize = mCurrentEvents.size(); - if (null != lastEvent) { - if (VOWEL_E == lastEvent.mCodePoint) { - // We have a VOWEL_E at the end. There are four cases. - // - The vowel is the only code point in the buffer. Remove it. - // - The vowel is preceded by a ZWNJ. Remove both vowel E and ZWNJ. - // - The vowel is preceded by a consonant/medial, remove the consonant/medial. - // - In all other cases, it's strange, so just remove the last code point. - if (eventSize <= 1) { - mCurrentEvents.clear(); - } else { // eventSize >= 2 - final int previousCodePoint = mCurrentEvents.get(eventSize - 2).mCodePoint; - if (previousCodePoint == ZERO_WIDTH_NON_JOINER) { - mCurrentEvents.remove(eventSize - 1); - mCurrentEvents.remove(eventSize - 2); - } else if (isConsonantOrMedial(previousCodePoint)) { - mCurrentEvents.remove(eventSize - 2); - } else { - mCurrentEvents.remove(eventSize - 1); - } - } - return Event.createConsumedEvent(newEvent); - } else if (eventSize > 0) { - mCurrentEvents.remove(eventSize - 1); - return Event.createConsumedEvent(newEvent); - } - } - } - // This character is not part of the combining scheme, so we should reset everything. - if (mCurrentEvents.size() > 0) { - // If we have events in flight, then add the new event and return the resulting event. - mCurrentEvents.add(newEvent); - return clearAndGetResultingEvent(null); - } else { - // If we don't have any events in flight, then just pass this one through. - return newEvent; - } - } - - @Override - public CharSequence getCombiningStateFeedback() { - return getCharSequence(); - } - - @Override - public void reset() { - mCurrentEvents.clear(); - } -} |