aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/event
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/event')
-rw-r--r--java/src/com/android/inputmethod/event/CombinerChain.java45
-rw-r--r--java/src/com/android/inputmethod/event/DeadKeyCombiner.java265
-rw-r--r--java/src/com/android/inputmethod/event/Event.java33
-rw-r--r--java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java7
-rw-r--r--java/src/com/android/inputmethod/event/MyanmarReordering.java261
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();
- }
-}