[HW12] Use the consumed status of the Event.

Change-Id: I1619f6132f8f71bc1291fd6a5604a5e1e3431ae2
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index f69bf4f..2d2731f 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -82,6 +82,13 @@
         }
     }
 
+    private void updateStateFeedback() {
+        mStateFeedback.clear();
+        for (int i = mCombiners.size() - 1; i >= 0; --i) {
+            mStateFeedback.append(mCombiners.get(i).getCombiningStateFeedback());
+        }
+    }
+
     /**
      * Process an event through the combining chain, and return a processed event to apply.
      * @param previousEvents the list of previous events in this composition
@@ -97,7 +104,13 @@
             // 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 (event.isConsumed()) {
+                // If the event is consumed, then we don't pass it to subsequent combiners:
+                // they should not see it at all.
+                break;
+            }
         }
+        updateStateFeedback();
         return event;
     }
 
@@ -121,10 +134,7 @@
                 }
             }
         }
-        mStateFeedback.clear();
-        for (int i = mCombiners.size() - 1; i >= 0; --i) {
-            mStateFeedback.append(mCombiners.get(i).getCombiningStateFeedback());
-        }
+        updateStateFeedback();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
index d816247..4f3f4d2 100644
--- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
+++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
@@ -36,10 +36,16 @@
     @Nonnull
     public Event processEvent(final ArrayList<Event> previousEvents, final Event event) {
         if (TextUtils.isEmpty(mDeadSequence)) {
+            // No dead char is currently being tracked: this is the most common case.
             if (event.isDead()) {
+                // The event was a dead key. Start tracking it.
                 mDeadSequence.appendCodePoint(event.mCodePoint);
+                return Event.createConsumedEvent(event);
             }
-            return Event.createConsumedEvent(event);
+            // Regular keystroke when not keeping track of a dead key. Simply said, there are
+            // 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.
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java
index 98c8274..f02f788 100644
--- a/java/src/com/android/inputmethod/event/Event.java
+++ b/java/src/com/android/inputmethod/event/Event.java
@@ -227,6 +227,7 @@
      * @return an identical event marked as consumed.
      */
     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,
                 source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags | FLAG_CONSUMED,
                 source.mNextEvent);
@@ -267,6 +268,9 @@
     }
 
     public CharSequence getTextToCommit() {
+        if (isConsumed()) {
+            return ""; // A consumed event should input no text.
+        }
         switch (mEventType) {
         case EVENT_TYPE_MODE_KEY:
         case EVENT_TYPE_NOT_HANDLED:
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 3b69048..32d1fe3 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -184,6 +184,9 @@
     @Nonnull
     public Event processEvent(final Event event) {
         final Event processedEvent = mCombinerChain.processEvent(mEvents, event);
+        // The retained state of the combiner chain may have changed while processing the event,
+        // so we need to update our cache.
+        refreshTypedWordCache();
         mEvents.add(event);
         return processedEvent;
     }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index e2feb7c..dee7cd4 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -206,7 +206,7 @@
             final int keyboardShiftMode,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
-        final String rawText = event.mText.toString();
+        final String rawText = event.getTextToCommit().toString();
         final InputTransaction inputTransaction = new InputTransaction(settingsValues, event,
                 SystemClock.uptimeMillis(), mSpaceState,
                 getActualCapsMode(settingsValues, keyboardShiftMode));
@@ -416,6 +416,8 @@
         mLastKeyTime = inputTransaction.mTimestamp;
         mConnection.beginBatchEdit();
         if (!mWordComposer.isComposingWord()) {
+            // TODO: is this useful? It doesn't look like it should be done here, but rather after
+            // a word is committed.
             mIsAutoCorrectionIndicatorOn = false;
         }
 
@@ -425,7 +427,21 @@
         }
 
         boolean didAutoCorrect = false;
-        if (processedEvent.isFunctionalKeyEvent()) {
+        if (processedEvent.isConsumed()) {
+            // A consumed event may have text to commit and an update to the composing state, so
+            // we evaluate both. With some combiners, it's possible than an event contains both
+            // and we enter both of the following if clauses.
+            final CharSequence textToCommit = processedEvent.getTextToCommit();
+            if (!TextUtils.isEmpty(textToCommit)) {
+                mConnection.commitText(textToCommit, 1);
+                inputTransaction.setDidAffectContents();
+            }
+            if (mWordComposer.isComposingWord()) {
+                mConnection.setComposingText(mWordComposer.getTypedWord(), 1);
+                inputTransaction.setDidAffectContents();
+                inputTransaction.setRequiresUpdateSuggestions();
+            }
+        } else if (processedEvent.isFunctionalKeyEvent()) {
             // A special key, like delete, shift, emoji, or the settings key.
             switch (processedEvent.mKeyCode) {
             case Constants.CODE_DELETE: