[CB11] Get the result of the combination

Change-Id: I165ec4a24f5b1334d55c14948e4d942ec40eb33b
diff --git a/java/src/com/android/inputmethod/event/Combiner.java b/java/src/com/android/inputmethod/event/Combiner.java
index c3869a2..bdc7612 100644
--- a/java/src/com/android/inputmethod/event/Combiner.java
+++ b/java/src/com/android/inputmethod/event/Combiner.java
@@ -34,4 +34,10 @@
      * @return the resulting 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();
 }
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index 0e01c81..cf2a4d1 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.event;
 
+import android.text.SpannableStringBuilder;
+
 import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import java.util.ArrayList;
@@ -33,8 +35,10 @@
  * a colored background.
  */
 public class CombinerChain {
-    // TODO: Create an object type to represent input material + visual feedback + decoding state
-
+    // 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;
 
     /**
@@ -50,9 +54,15 @@
         mCombiners = CollectionUtils.newArrayList();
         // The dead key combiner is always active, and always first
         mCombiners.add(new DeadKeyCombiner());
+        mCombinedText = new StringBuilder();
+        mStateFeedback = new SpannableStringBuilder();
     }
 
-    // Pass a new event through the whole chain.
+    /**
+     * 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<Event>(previousEvents);
         Event event = newEvent;
@@ -62,8 +72,24 @@
             event = combiner.processEvent(modifiablePreviousEvents, event);
             if (null == event) {
                 // Combiners return null if they eat the event.
-                return;
+                break;
             }
         }
+        if (null != event) {
+            mCombinedText.append(event.getTextToCommit());
+        }
+        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);
     }
 }
diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
index f77ce63..f891017 100644
--- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
+++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
@@ -61,4 +61,9 @@
             }
         }
     }
+
+    @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 db40234..2bfe073 100644
--- a/java/src/com/android/inputmethod/event/Event.java
+++ b/java/src/com/android/inputmethod/event/Event.java
@@ -18,6 +18,7 @@
 
 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.
@@ -223,4 +224,19 @@
     public boolean isHandled() {
         return EVENT_NOT_HANDLED != mType;
     }
+
+    public CharSequence getTextToCommit() {
+        switch (mType) {
+        case EVENT_MODE_KEY:
+        case EVENT_NOT_HANDLED:
+            return "";
+        case EVENT_INPUT_KEYPRESS:
+        case EVENT_TOGGLE:
+            return StringUtils.newSingleCodePointString(mCodePoint);
+        case EVENT_GESTURE:
+        case EVENT_SOFTWARE_GENERATED_STRING:
+            return mText;
+        }
+        throw new RuntimeException("Unknown event type: " + mType);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 29382fe..d55a773 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -193,7 +193,10 @@
         final int keyY = event.mY;
         final int newIndex = size();
         mCombinerChain.processEvent(mEvents, event);
-        mTypedWord.appendCodePoint(primaryCode);
+        // TODO: remove mTypedWord and compute it dynamically when necessary. We also need to
+        // make the views of the composing word a SpannableString.
+        mTypedWord.replace(0, mTypedWord.length(),
+                mCombinerChain.getComposingWordWithCombiningFeedback().toString());
         mEvents.add(event);
         refreshSize();
         mCursorPositionWithinWord = mCodePointSize;
diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
index 66ad60c..e70ede4 100644
--- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
+++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
@@ -21,6 +21,12 @@
 import java.util.ArrayList;
 
 public class CombinerChain {
+    private StringBuilder mComposingWord = new StringBuilder();
     public CombinerChain(final Combiner... combinerList) {}
-    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {}
+    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
+        mComposingWord.append(newEvent.getTextToCommit());
+    }
+    public CharSequence getComposingWordWithCombiningFeedback() {
+        return mComposingWord;
+    }
 }