Fix a bug where changing languages kills the composition.

Bug: 14926896
Change-Id: Id2a06c68a51f833db4d8bbc858a232c2696afb6e
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index 990f7de..9e7f04d 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -56,18 +56,20 @@
      *
      * 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.
+     * 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 Combiner... combinerList) {
+    public CombinerChain(final String initialText, final Combiner... combinerList) {
         mCombiners = CollectionUtils.newArrayList();
         // 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();
+        mCombinedText = new StringBuilder(initialText);
         mStateFeedback = new SpannableStringBuilder();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index cdee496..ac69729 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -80,7 +80,7 @@
     private boolean mIsFirstCharCapitalized;
 
     public WordComposer() {
-        mCombinerChain = new CombinerChain();
+        mCombinerChain = new CombinerChain("");
         mEvents = CollectionUtils.newArrayList();
         mAutoCorrection = null;
         mIsResumed = false;
@@ -92,18 +92,17 @@
     }
 
     /**
-     * Restart input with a new combining spec.
+     * Restart the combiners, possibly with a new spec.
      * @param combiningSpec The spec string for combining. This is found in the extra value.
      */
-    public void restart(final String combiningSpec) {
+    public void restartCombining(final String combiningSpec) {
         final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec;
-        if (nonNullCombiningSpec.equals(mCombiningSpec)) {
-            mCombinerChain.reset();
-        } else {
-            mCombinerChain = new CombinerChain(CombinerChain.createCombiners(nonNullCombiningSpec));
+        if (!nonNullCombiningSpec.equals(mCombiningSpec)) {
+            mCombinerChain = new CombinerChain(
+                    mCombinerChain.getComposingWordWithCombiningFeedback().toString(),
+                    CombinerChain.createCombiners(nonNullCombiningSpec));
             mCombiningSpec = nonNullCombiningSpec;
         }
-        reset();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 1156c77..ea58abc 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -127,7 +127,7 @@
     public void startInput(final boolean restarting, final EditorInfo editorInfo,
             final String combiningSpec) {
         mEnteredText = null;
-        mWordComposer.restart(combiningSpec);
+        mWordComposer.restartCombining(combiningSpec);
         resetComposingState(true /* alsoResetLastComposedWord */);
         mDeleteCount = 0;
         mSpaceState = SpaceState.NONE;
@@ -150,7 +150,7 @@
      * @param combiningSpec the spec string for the combining rules
      */
     public void onSubtypeChanged(final String combiningSpec) {
-        mWordComposer.restart(combiningSpec);
+        mWordComposer.restartCombining(combiningSpec);
     }
 
     /**
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 29423e8..a944416 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -600,4 +600,16 @@
         assertEquals("type words letter by letter", EXPECTED_RESULT,
                 mEditText.getText().toString());
     }
+
+    public void testSwitchLanguages() {
+        final String WORD_TO_TYPE_FIRST_PART = "com";
+        final String WORD_TO_TYPE_SECOND_PART = "md ";
+        final String EXPECTED_RESULT = "comme ";
+        changeLanguage("en");
+        type(WORD_TO_TYPE_FIRST_PART);
+        changeLanguage("fr");
+        type(WORD_TO_TYPE_SECOND_PART);
+        assertEquals("Composing continues after switching languages", EXPECTED_RESULT,
+                mEditText.getText().toString());
+    }
 }
diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
index 47f781e..538d759 100644
--- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
+++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
@@ -28,8 +28,10 @@
 // TODO: there should not be a dependency to this in dicttool, so there
 // should be a sensible way to separate them cleanly.
 public class CombinerChain {
-    private StringBuilder mComposingWord = new StringBuilder();
-    public CombinerChain(final Combiner... combinerList) {}
+    private StringBuilder mComposingWord;
+    public CombinerChain(final String initialText, final Combiner... combinerList) {
+        mComposingWord = new StringBuilder(initialText);
+    }
 
     public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
         mComposingWord.append(newEvent.getTextToCommit());