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/Combiner.java25
-rw-r--r--java/src/com/android/inputmethod/event/CombinerChain.java155
-rw-r--r--java/src/com/android/inputmethod/event/DeadKeyCombiner.java27
-rw-r--r--java/src/com/android/inputmethod/event/Event.java232
-rw-r--r--java/src/com/android/inputmethod/event/EventDecoderSpec.java26
-rw-r--r--java/src/com/android/inputmethod/event/EventInterpreter.java133
-rw-r--r--java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java24
-rw-r--r--java/src/com/android/inputmethod/event/InputTransaction.java100
-rw-r--r--java/src/com/android/inputmethod/event/MyanmarReordering.java258
-rw-r--r--java/src/com/android/inputmethod/event/SoftwareEventDecoder.java29
-rw-r--r--java/src/com/android/inputmethod/event/SoftwareKeyboardEventDecoder.java27
11 files changed, 777 insertions, 259 deletions
diff --git a/java/src/com/android/inputmethod/event/Combiner.java b/java/src/com/android/inputmethod/event/Combiner.java
index ab6b70c04..8b808c6b3 100644
--- a/java/src/com/android/inputmethod/event/Combiner.java
+++ b/java/src/com/android/inputmethod/event/Combiner.java
@@ -16,14 +16,33 @@
package com.android.inputmethod.event;
+import java.util.ArrayList;
+
/**
- * A generic interface for combiners.
+ * A generic interface for combiners. Combiners are objects that transform chains of input events
+ * into committable strings and manage feedback to show to the user on the combining state.
*/
public interface Combiner {
/**
- * Combine an event with the existing state and return the new event.
+ * Process an event, possibly combining it with the existing state and return the new event.
+ *
+ * If this event does not result in any new event getting passed down the chain, this method
+ * returns null. It may also modify the previous event list if appropriate.
+ *
+ * @param previousEvents the previous events in this composition.
* @param event the event to combine with the existing state.
* @return the resulting event.
*/
- Event combine(Event event);
+ Event processEvent(ArrayList<Event> previousEvents, Event event);
+
+ /**
+ * Get the feedback that should be shown to the user for the current state of this combiner.
+ * @return A CharSequence representing the feedback to show users. It may include styles.
+ */
+ CharSequence getCombiningStateFeedback();
+
+ /**
+ * Reset the state of this combiner, for example when the cursor was moved.
+ */
+ void reset();
}
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
new file mode 100644
index 000000000..61bc11b39
--- /dev/null
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -0,0 +1,155 @@
+/*
+ * 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 android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+
+import com.android.inputmethod.latin.Constants;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class implements the logic chain between receiving events and generating code points.
+ *
+ * Event sources are multiple. It may be a hardware keyboard, a D-PAD, a software keyboard,
+ * or any exotic input source.
+ * This class will orchestrate the composing chain that starts with an event as its input. Each
+ * composer will be given turns one after the other.
+ * The output is composed of two sequences of code points: the first, representing the already
+ * finished combining part, will be shown normally as the composing string, while the second is
+ * feedback on the composing state and will typically be shown with different styling such as
+ * a colored background.
+ */
+public class CombinerChain {
+ // The already combined text, as described above
+ private StringBuilder mCombinedText;
+ // The feedback on the composing state, as described above
+ 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.
+ *
+ * The combiner chain takes events as inputs and outputs code points and combining state.
+ * For example, if the input language is Japanese, the combining chain will typically perform
+ * kana conversion. This takes a string for initial text, taken to be present before the
+ * 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) {
+ 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();
+ }
+
+ public void reset() {
+ mCombinedText.setLength(0);
+ mStateFeedback.clear();
+ for (final Combiner c : mCombiners) {
+ c.reset();
+ }
+ }
+
+ /**
+ * Pass a new event through the whole chain.
+ * @param previousEvents the list of previous events in this composition
+ * @param newEvent the new event to process
+ */
+ public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
+ final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents);
+ Event event = newEvent;
+ for (final Combiner combiner : mCombiners) {
+ // A combiner can never return more than one event; it can return several
+ // code points, but they should be encapsulated within one event.
+ event = combiner.processEvent(modifiablePreviousEvents, event);
+ if (null == event) {
+ // Combiners return null if they eat the event.
+ break;
+ }
+ }
+ if (null != event) {
+ // TODO: figure out the generic way of doing this
+ if (Constants.CODE_DELETE == event.mKeyCode) {
+ final int length = mCombinedText.length();
+ if (length > 0) {
+ final int lastCodePoint = mCombinedText.codePointBefore(length);
+ mCombinedText.delete(length - Character.charCount(lastCodePoint), length);
+ }
+ } else {
+ final CharSequence textToCommit = event.getTextToCommit();
+ if (!TextUtils.isEmpty(textToCommit)) {
+ mCombinedText.append(textToCommit);
+ }
+ }
+ }
+ mStateFeedback.clear();
+ for (int i = mCombiners.size() - 1; i >= 0; --i) {
+ mStateFeedback.append(mCombiners.get(i).getCombiningStateFeedback());
+ }
+ }
+
+ /**
+ * Get the char sequence that should be displayed as the composing word. It may include
+ * styling spans.
+ */
+ public CharSequence getComposingWordWithCombiningFeedback() {
+ 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 52987d571..bef4d8594 100644
--- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
+++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
@@ -21,14 +21,17 @@ import android.view.KeyCharacterMap;
import com.android.inputmethod.latin.Constants;
+import java.util.ArrayList;
+
/**
* A combiner that handles dead keys.
*/
public class DeadKeyCombiner implements Combiner {
+ // TODO: make this a list of events instead
final StringBuilder mDeadSequence = new StringBuilder();
@Override
- public Event combine(final Event event) {
+ public Event processEvent(final ArrayList<Event> previousEvents, final Event event) {
if (null == event) return null; // Just in case some combiner is broken
if (TextUtils.isEmpty(mDeadSequence)) {
if (event.isDead()) {
@@ -43,19 +46,33 @@ public class DeadKeyCombiner implements Combiner {
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 committable
+ // 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.
- return Event.createCommittableEvent(deadCodePoint,
- Constants.CODE_SPACE == event.mCodePoint ? null : event /* next */);
+ // 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 */);
} else {
// We could combine the characters.
- return Event.createCommittableEvent(resultingCodePoint, null /* next */);
+ return Event.createHardwareKeypressEvent(resultingCodePoint, event.mKeyCode,
+ null /* next */, false /* isKeyRepeat */);
}
}
}
+ @Override
+ public void reset() {
+ mDeadSequence.setLength(0);
+ }
+
+ @Override
+ public CharSequence getCombiningStateFeedback() {
+ return mDeadSequence;
+ }
}
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java
index 1f3320eb7..d257441e0 100644
--- a/java/src/com/android/inputmethod/event/Event.java
+++ b/java/src/com/android/inputmethod/event/Event.java
@@ -16,6 +16,10 @@
package com.android.inputmethod.event;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.utils.StringUtils;
+
/**
* Class representing a generic input event as handled by Latin IME.
*
@@ -32,62 +36,234 @@ public class Event {
// Should the types below be represented by separate classes instead? It would be cleaner
// but probably a bit too much
// An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard.
- final public static int EVENT_NOT_HANDLED = 0;
- // A character that is already final, for example pressing an alphabetic character on a
- // hardware qwerty keyboard.
- final public static int EVENT_COMMITTABLE = 1;
- // A dead key, which means a character that should combine with what is coming next. Examples
- // include the "^" character on an azerty keyboard which combines with "e" to make "ê", or
- // AltGr+' on a dvorak international keyboard which combines with "e" to make "é". This is
- // true regardless of the language or combining mode, and should be seen as a property of the
- // key - a dead key followed by another key with which it can combine should be regarded as if
- // the keyboard actually had such a key.
- final public static int EVENT_DEAD = 2;
+ final public static int EVENT_TYPE_NOT_HANDLED = 0;
+ // A key press that is part of input, for example pressing an alphabetic character on a
+ // hardware qwerty keyboard. It may be part of a sequence that will be re-interpreted later
+ // through combination.
+ final public static int EVENT_TYPE_INPUT_KEYPRESS = 1;
// A toggle event is triggered by a key that affects the previous character. An example would
// be a numeric key on a 10-key keyboard, which would toggle between 1 - a - b - c with
// repeated presses.
- final public static int EVENT_TOGGLE = 3;
+ final public static int EVENT_TYPE_TOGGLE = 2;
// A mode event instructs the combiner to change modes. The canonical example would be the
// hankaku/zenkaku key on a Japanese keyboard, or even the caps lock key on a qwerty keyboard
// if handled at the combiner level.
- final public static int EVENT_MODE_KEY = 4;
+ final public static int EVENT_TYPE_MODE_KEY = 3;
+ // An event corresponding to a gesture.
+ final public static int EVENT_TYPE_GESTURE = 4;
+ // An event corresponding to the manual pick of a suggestion.
+ 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;
+
+ // 0 is a valid code point, so we use -1 here.
+ final public static int NOT_A_CODE_POINT = -1;
+ // -1 is a valid key code, so we use 0 here.
+ final public static int NOT_A_KEY_CODE = 0;
- final private static int NOT_A_CODE_POINT = 0;
+ final private static int FLAG_NONE = 0;
+ // This event is a dead character, usually input by a dead key. Examples include dead-acute
+ // or dead-abovering.
+ final private static int FLAG_DEAD = 0x1;
+ // This event is coming from a key repeat, software or hardware.
+ final private static int FLAG_REPEAT = 0x2;
- final private int mType; // The type of event - one of the constants above
+ final private int mEventType; // The type of event - one of the constants above
// The code point associated with the event, if relevant. This is a unicode code point, and
// has nothing to do with other representations of the key. It is only relevant if this event
- // is the right type: COMMITTABLE or DEAD or TOGGLE, but for a mode key like hankaku/zenkaku or
- // ctrl, there is no code point associated so this should be NOT_A_CODE_POINT to avoid
- // unintentional use of its value when it's not relevant.
+ // is of KEYPRESS type, but for a mode key like hankaku/zenkaku or ctrl, there is no code point
+ // associated so this should be NOT_A_CODE_POINT to avoid unintentional use of its value when
+ // it's not relevant.
final public int mCodePoint;
+
+ // If applicable, this contains the string that should be input.
+ final public CharSequence mText;
+
+ // The key code associated with the event, if relevant. This is relevant whenever this event
+ // has been triggered by a key press, but not for a gesture for example. This has conceptually
+ // no link to the code point, although keys that enter a straight code point may often set
+ // this to be equal to mCodePoint for convenience. If this is not a key, this must contain
+ // NOT_A_KEY_CODE.
+ final public int mKeyCode;
+
+ // Coordinates of the touch event, if relevant. If useful, we may want to replace this with
+ // a MotionEvent or something in the future. This is only relevant when the keypress is from
+ // a software keyboard obviously, unless there are touch-sensitive hardware keyboards in the
+ // future or some other awesome sauce.
+ final public int mX;
+ final public int mY;
+
+ // Some flags that can't go into the key code. It's a bit field of FLAG_*
+ final private int mFlags;
+
+ // If this is of type EVENT_TYPE_SUGGESTION_PICKED, this must not be null (and must be null in
+ // other cases).
+ final public SuggestedWordInfo mSuggestedWordInfo;
+
// The next event, if any. Null if there is no next event yet.
final public Event mNextEvent;
// This method is private - to create a new event, use one of the create* utility methods.
- private Event(final int type, final int codePoint, final Event next) {
- mType = type;
+ private Event(final int type, final CharSequence text, final int codePoint, final int keyCode,
+ final int x, final int y, final SuggestedWordInfo suggestedWordInfo, final int flags,
+ final Event next) {
+ mEventType = type;
+ mText = text;
mCodePoint = codePoint;
+ mKeyCode = keyCode;
+ mX = x;
+ mY = y;
+ mSuggestedWordInfo = suggestedWordInfo;
+ mFlags = flags;
mNextEvent = next;
+ // Sanity checks
+ // mSuggestedWordInfo is non-null if and only if the type is SUGGESTION_PICKED
+ if (EVENT_TYPE_SUGGESTION_PICKED == mEventType) {
+ if (null == mSuggestedWordInfo) {
+ throw new RuntimeException("Wrong event: SUGGESTION_PICKED event must have a "
+ + "non-null SuggestedWordInfo");
+ }
+ } else {
+ if (null != mSuggestedWordInfo) {
+ throw new RuntimeException("Wrong event: only SUGGESTION_PICKED events may have " +
+ "a non-null SuggestedWordInfo");
+ }
+ }
+ }
+
+ 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);
+ }
+
+ 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,
+ Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
+ null /* suggestedWordInfo */, isKeyRepeat ? FLAG_REPEAT : FLAG_NONE, next);
+ }
+
+ // This creates an input event for a dead character. @see {@link #FLAG_DEAD}
+ 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,
+ Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
+ null /* suggestedWordInfo */, FLAG_DEAD, next);
}
- public static Event createDeadEvent(final int codePoint, final Event next) {
- return new Event(EVENT_DEAD, codePoint, next);
+ /**
+ * Create an input event with nothing but a code point. This is the most basic possible input
+ * event; it contains no information on many things the IME requires to function correctly,
+ * so avoid using it unless really nothing is known about this input.
+ * @param codePoint the code point.
+ * @return an event for this code point.
+ */
+ 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,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
+ null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
}
- public static Event createCommittableEvent(final int codePoint, final Event next) {
- return new Event(EVENT_COMMITTABLE, codePoint, next);
+ /**
+ * Creates an input event with a code point and x, y coordinates. This is typically used when
+ * resuming a previously-typed word, when the coordinates are still known.
+ * @param codePoint the code point to input.
+ * @param x the X coordinate.
+ * @param y the Y coordinate.
+ * @return an event for this code point and coordinates.
+ */
+ 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.
+ return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE,
+ x, y, null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
+ }
+
+ /**
+ * Creates an input event representing the manual pick of a suggestion.
+ * @return an event for this suggestion pick.
+ */
+ public static Event createSuggestionPickedEvent(final SuggestedWordInfo suggestedWordInfo) {
+ return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord,
+ NOT_A_CODE_POINT, NOT_A_KEY_CODE,
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE,
+ suggestedWordInfo, FLAG_NONE, null /* next */);
+ }
+
+ /**
+ * Creates an input event with a CharSequence. This is used by some software processes whose
+ * output is a string, possibly with styling. Examples include press on a multi-character key,
+ * or combination that outputs a string.
+ * @param text the CharSequence associated with this event.
+ * @param keyCode the key code, or NOT_A_KEYCODE if not applicable.
+ * @return an event for this text.
+ */
+ 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,
+ null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
+ }
+
+ /**
+ * Creates an input event representing the manual pick of a punctuation suggestion.
+ * @return an event for this suggestion pick.
+ */
+ public static Event createPunctuationSuggestionPickedEvent(
+ final SuggestedWordInfo suggestedWordInfo) {
+ final int primaryCode = suggestedWordInfo.mWord.charAt(0);
+ return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode,
+ NOT_A_KEY_CODE, Constants.SUGGESTION_STRIP_COORDINATE,
+ Constants.SUGGESTION_STRIP_COORDINATE, suggestedWordInfo, FLAG_NONE,
+ null /* next */);
}
public static Event createNotHandledEvent() {
- return new Event(EVENT_NOT_HANDLED, NOT_A_CODE_POINT, null);
+ 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,
+ null /* suggestedWordInfo */, FLAG_NONE, null);
}
- public boolean isCommittable() {
- return EVENT_COMMITTABLE == mType;
+ // Returns whether this is a function key like backspace, ctrl, settings... as opposed to keys
+ // that result in input like letters or space.
+ public boolean isFunctionalKeyEvent() {
+ // This logic may need to be refined in the future
+ return NOT_A_CODE_POINT == mCodePoint;
}
+ // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD}
public boolean isDead() {
- return EVENT_DEAD == mType;
+ return 0 != (FLAG_DEAD & mFlags);
+ }
+
+ public boolean isKeyRepeat() {
+ return 0 != (FLAG_REPEAT & mFlags);
+ }
+
+ // Returns whether this is a fake key press from the suggestion strip. This happens with
+ // punctuation signs selected from the suggestion strip.
+ public boolean isSuggestionStripPress() {
+ return EVENT_TYPE_SUGGESTION_PICKED == mEventType;
+ }
+
+ public boolean isHandled() {
+ return EVENT_TYPE_NOT_HANDLED != mEventType;
+ }
+
+ public CharSequence getTextToCommit() {
+ switch (mEventType) {
+ case EVENT_TYPE_MODE_KEY:
+ case EVENT_TYPE_NOT_HANDLED:
+ case EVENT_TYPE_TOGGLE:
+ return "";
+ case EVENT_TYPE_INPUT_KEYPRESS:
+ return StringUtils.newSingleCodePointString(mCodePoint);
+ case EVENT_TYPE_GESTURE:
+ case EVENT_TYPE_SOFTWARE_GENERATED_STRING:
+ case EVENT_TYPE_SUGGESTION_PICKED:
+ return mText;
+ }
+ throw new RuntimeException("Unknown event type: " + mEventType);
}
}
diff --git a/java/src/com/android/inputmethod/event/EventDecoderSpec.java b/java/src/com/android/inputmethod/event/EventDecoderSpec.java
deleted file mode 100644
index 303b4b4c9..000000000
--- a/java/src/com/android/inputmethod/event/EventDecoderSpec.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-/**
- * Class describing a decoder chain. This will depend on the language and the input medium (soft
- * or hard keyboard for example).
- */
-public class EventDecoderSpec {
- public EventDecoderSpec() {
- }
-}
diff --git a/java/src/com/android/inputmethod/event/EventInterpreter.java b/java/src/com/android/inputmethod/event/EventInterpreter.java
deleted file mode 100644
index 726b9206b..000000000
--- a/java/src/com/android/inputmethod/event/EventInterpreter.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.util.SparseArray;
-import android.view.KeyEvent;
-
-import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.LatinIME;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-
-import java.util.ArrayList;
-
-/**
- * This class implements the logic between receiving events and generating code points.
- *
- * Event sources are multiple. It may be a hardware keyboard, a D-PAD, a software keyboard,
- * or any exotic input source.
- * This class will orchestrate the decoding chain that starts with an event and ends up with
- * a stream of code points + decoding state.
- */
-public class EventInterpreter {
- // TODO: Implement an object pool for events, as we'll create a lot of them
- // TODO: Create a combiner
- // TODO: Create an object type to represent input material + visual feedback + decoding state
- // TODO: Create an interface to call back to Latin IME through the above object
-
- final EventDecoderSpec mDecoderSpec;
- final SparseArray<HardwareEventDecoder> mHardwareEventDecoders;
- final SoftwareEventDecoder mSoftwareEventDecoder;
- final LatinIME mLatinIme;
- final ArrayList<Combiner> mCombiners;
-
- /**
- * Create a default interpreter.
- *
- * This creates a default interpreter that does nothing. A default interpreter should normally
- * only be used for fallback purposes, when we really don't know what we want to do with input.
- *
- * @param latinIme a reference to the ime.
- */
- public EventInterpreter(final LatinIME latinIme) {
- this(null, latinIme);
- }
-
- /**
- * Create an event interpreter according to a specification.
- *
- * The specification contains information about what to do with events. Typically, it will
- * contain information about the type of keyboards - for example, if hardware keyboard(s) is/are
- * attached, their type will be included here so that the decoder knows what to do with each
- * keypress (a 10-key keyboard is not handled like a qwerty-ish keyboard).
- * It also contains information for combining characters. For example, if the input language
- * is Japanese, the specification will typically request kana conversion.
- * Also note that the specification can be null. This means that we need to create a default
- * interpreter that does no specific combining, and assumes the most common cases.
- *
- * @param specification the specification for event interpretation. null for default.
- * @param latinIme a reference to the ime.
- */
- public EventInterpreter(final EventDecoderSpec specification, final LatinIME latinIme) {
- mDecoderSpec = null != specification ? specification : new EventDecoderSpec();
- // For both, we expect to have only one decoder in almost all cases, hence the default
- // capacity of 1.
- mHardwareEventDecoders = new SparseArray<HardwareEventDecoder>(1);
- mSoftwareEventDecoder = new SoftwareKeyboardEventDecoder();
- mCombiners = CollectionUtils.newArrayList();
- mCombiners.add(new DeadKeyCombiner());
- mLatinIme = latinIme;
- }
-
- // Helper method to decode a hardware key event into a generic event, and execute any
- // necessary action.
- public boolean onHardwareKeyEvent(final KeyEvent hardwareKeyEvent) {
- final Event decodedEvent = getHardwareKeyEventDecoder(hardwareKeyEvent.getDeviceId())
- .decodeHardwareKey(hardwareKeyEvent);
- return onEvent(decodedEvent);
- }
-
- public boolean onSoftwareEvent() {
- final Event decodedEvent = getSoftwareEventDecoder().decodeSoftwareEvent();
- return onEvent(decodedEvent);
- }
-
- private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) {
- final HardwareEventDecoder decoder = mHardwareEventDecoders.get(deviceId);
- if (null != decoder) return decoder;
- // TODO: create the decoder according to the specification
- final HardwareEventDecoder newDecoder = new HardwareKeyboardEventDecoder(deviceId);
- mHardwareEventDecoders.put(deviceId, newDecoder);
- return newDecoder;
- }
-
- private SoftwareEventDecoder getSoftwareEventDecoder() {
- // Within the context of Latin IME, since we never present several software interfaces
- // at the time, we should never need multiple software event decoders at a time.
- return mSoftwareEventDecoder;
- }
-
- private boolean onEvent(final Event event) {
- Event currentlyProcessingEvent = event;
- boolean processed = false;
- for (int i = 0; i < mCombiners.size(); ++i) {
- currentlyProcessingEvent = mCombiners.get(i).combine(event);
- }
- while (null != currentlyProcessingEvent) {
- if (currentlyProcessingEvent.isCommittable()) {
- mLatinIme.onCodeInput(currentlyProcessingEvent.mCodePoint,
- Constants.EXTERNAL_KEYBOARD_COORDINATE,
- Constants.EXTERNAL_KEYBOARD_COORDINATE);
- processed = true;
- } else if (event.isDead()) {
- processed = true;
- }
- currentlyProcessingEvent = currentlyProcessingEvent.mNextEvent;
- }
- return processed;
- }
-}
diff --git a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
index 720d07433..c61f45efa 100644
--- a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
+++ b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
@@ -46,28 +46,36 @@ public class HardwareKeyboardEventDecoder implements HardwareEventDecoder {
// do not necessarily map to a unicode character. This represents a physical key, like
// the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock.
final int keyCode = keyEvent.getKeyCode();
+ final boolean isKeyRepeat = (0 != keyEvent.getRepeatCount());
if (KeyEvent.KEYCODE_DEL == keyCode) {
- return Event.createCommittableEvent(Constants.CODE_DELETE, null /* next */);
+ return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, Constants.CODE_DELETE,
+ null /* next */, isKeyRepeat);
}
if (keyEvent.isPrintingKey() || KeyEvent.KEYCODE_SPACE == keyCode
|| KeyEvent.KEYCODE_ENTER == keyCode) {
if (0 != (codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT)) {
// A dead key.
return Event.createDeadEvent(
- codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT_MASK, null /* next */);
+ codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode,
+ null /* next */);
}
if (KeyEvent.KEYCODE_ENTER == keyCode) {
// The Enter key. If the Shift key is not being pressed, this should send a
// CODE_ENTER to trigger the action if any, or a carriage return otherwise. If the
// Shift key is being pressed, this should send a CODE_SHIFT_ENTER and let
// Latin IME decide what to do with it.
- return Event.createCommittableEvent(keyEvent.isShiftPressed()
- ? Constants.CODE_SHIFT_ENTER : Constants.CODE_ENTER,
- null /* next */);
+ 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);
+ }
}
- // If not Enter, then we have a committable character. This should be committed
- // right away, taking into account the current state.
- return Event.createCommittableEvent(codePointAndFlags, null /* next */);
+ // 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.
+ return Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */,
+ isKeyRepeat);
}
return Event.createNotHandledEvent();
}
diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java
new file mode 100644
index 000000000..cdff265c6
--- /dev/null
+++ b/java/src/com/android/inputmethod/event/InputTransaction.java
@@ -0,0 +1,100 @@
+/*
+ * 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.settings.SettingsValues;
+
+/**
+ * An object encapsulating a single transaction for input.
+ */
+public class InputTransaction {
+ // UPDATE_LATER is stronger than UPDATE_NOW. The reason for this is, if we have to update later,
+ // it's because something will change that we can't evaluate now, which means that even if we
+ // re-evaluate now we'll have to do it again later. The only case where that wouldn't apply
+ // would be if we needed to update now to find out the new state right away, but then we
+ // can't do it with this deferred mechanism anyway.
+ public static final int SHIFT_NO_UPDATE = 0;
+ public static final int SHIFT_UPDATE_NOW = 1;
+ public static final int SHIFT_UPDATE_LATER = 2;
+
+ // Initial conditions
+ public final SettingsValues mSettingsValues;
+ public final Event mEvent;
+ public final long mTimestamp;
+ public final int mSpaceState;
+ public final int mShiftState;
+
+ // Outputs
+ private int mRequiredShiftUpdate = SHIFT_NO_UPDATE;
+ private boolean mRequiresUpdateSuggestions = false;
+ private boolean mDidAffectContents = false;
+
+ public InputTransaction(final SettingsValues settingsValues, final Event event,
+ final long timestamp, final int spaceState, final int shiftState) {
+ mSettingsValues = settingsValues;
+ mEvent = event;
+ mTimestamp = timestamp;
+ mSpaceState = spaceState;
+ mShiftState = shiftState;
+ }
+
+ /**
+ * Indicate that this transaction requires some type of shift update.
+ * @param updateType What type of shift update this requires.
+ */
+ public void requireShiftUpdate(final int updateType) {
+ mRequiredShiftUpdate = Math.max(mRequiredShiftUpdate, updateType);
+ }
+
+ /**
+ * Gets what type of shift update this transaction requires.
+ * @return The shift update type.
+ */
+ public int getRequiredShiftUpdate() {
+ return mRequiredShiftUpdate;
+ }
+
+ /**
+ * Indicate that this transaction requires updating the suggestions.
+ */
+ public void setRequiresUpdateSuggestions() {
+ mRequiresUpdateSuggestions = true;
+ }
+
+ /**
+ * Find out whether this transaction requires updating the suggestions.
+ * @return Whether this transaction requires updating the suggestions.
+ */
+ public boolean requiresUpdateSuggestions() {
+ return mRequiresUpdateSuggestions;
+ }
+
+ /**
+ * Indicate that this transaction affected the contents of the editor.
+ */
+ public void setDidAffectContents() {
+ mDidAffectContents = true;
+ }
+
+ /**
+ * Find out whether this transaction affected contents of the editor.
+ * @return Whether this transaction affected contents of the editor.
+ */
+ public boolean didAffectContents() {
+ return mDidAffectContents;
+ }
+}
diff --git a/java/src/com/android/inputmethod/event/MyanmarReordering.java b/java/src/com/android/inputmethod/event/MyanmarReordering.java
new file mode 100644
index 000000000..32919932d
--- /dev/null
+++ b/java/src/com/android/inputmethod/event/MyanmarReordering.java
@@ -0,0 +1,258 @@
+/*
+ * 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;
+
+/**
+ * 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. Null if none.
+ */
+ 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 ? null
+ : Event.createSoftwareTextEvent(combinedText, Event.NOT_A_KEY_CODE);
+ }
+
+ @Override
+ 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 null;
+ } 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 null;
+ } 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 null;
+ }
+ // 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 null;
+ } 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 null;
+ } 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 null;
+ }
+ // 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 null;
+ } else if (eventSize > 0) {
+ mCurrentEvents.remove(eventSize - 1);
+ return null;
+ }
+ }
+ }
+ // 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();
+ }
+}
diff --git a/java/src/com/android/inputmethod/event/SoftwareEventDecoder.java b/java/src/com/android/inputmethod/event/SoftwareEventDecoder.java
deleted file mode 100644
index d81ee0b37..000000000
--- a/java/src/com/android/inputmethod/event/SoftwareEventDecoder.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-/**
- * An event decoder for events out of a software keyboard.
- *
- * This defines the interface for an event decoder that supports events out of a software keyboard.
- * This differs significantly from hardware keyboard event decoders in several respects. First,
- * a software keyboard does not have a scancode/layout system; the keypresses that insert
- * characters output unicode characters directly.
- */
-public interface SoftwareEventDecoder extends EventDecoder {
- public Event decodeSoftwareEvent();
-}
diff --git a/java/src/com/android/inputmethod/event/SoftwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/SoftwareKeyboardEventDecoder.java
deleted file mode 100644
index de91567c7..000000000
--- a/java/src/com/android/inputmethod/event/SoftwareKeyboardEventDecoder.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-/**
- * A decoder for events from software keyboard, like the ones displayed by Latin IME.
- */
-public class SoftwareKeyboardEventDecoder implements SoftwareEventDecoder {
- @Override
- public Event decodeSoftwareEvent() {
- return null;
- }
-}