Merge "Refactor KeyboardTextsSet class"
diff --git a/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml b/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml
index ab99ec5..76ac6bb 100644
--- a/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml
+++ b/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml
@@ -23,7 +23,7 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
         >
             <Key
                 latin:keySpec="["
diff --git a/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml b/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml
index 5443396..f18fb50 100644
--- a/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml
+++ b/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml
@@ -23,7 +23,7 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
         >
             <Key
                 latin:keySpec=";"
diff --git a/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml b/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml
index c95ca2e..ff1a2c8 100644
--- a/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml
+++ b/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml
@@ -23,7 +23,7 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
         >
             <Key
                 latin:keySpec=","
diff --git a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml
index 5389e22..5c7506e 100644
--- a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml
+++ b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml
@@ -21,87 +21,98 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <Key
-        latin:keySpec="`"
-        latin:keyHintLabel="~"
-        latin:additionalMoreKeys="~"
-        latin:keyStyle="hasShiftedLetterHintStyle" />
-    <Key
-        latin:keySpec="1"
-        latin:keyHintLabel="!"
-        latin:additionalMoreKeys="!"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_exclamation,!text/more_keys_for_symbols_1" />
-    <Key
-        latin:keySpec="2"
-        latin:keyHintLabel="\@"
-        latin:additionalMoreKeys="\@"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_2" />
-    <Key
-        latin:keySpec="3"
-        latin:keyHintLabel="\#"
-        latin:additionalMoreKeys="\#"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_3" />
-    <Key
-        latin:keySpec="4"
-        latin:keyHintLabel="$"
-        latin:additionalMoreKeys="$"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_4" />
-    <Key
-        latin:keySpec="5"
-        latin:keyHintLabel="%"
-        latin:additionalMoreKeys="\\%"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_5" />
-    <Key
-        latin:keySpec="6"
-        latin:keyHintLabel="^"
-        latin:additionalMoreKeys="^"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_6" />
-    <Key
-        latin:keySpec="7"
-        latin:keyHintLabel="&amp;"
-        latin:additionalMoreKeys="&amp;"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_7" />
-    <Key
-        latin:keySpec="8"
-        latin:keyHintLabel="*"
-        latin:additionalMoreKeys="*"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_8" />
-    <Key
-        latin:keySpec="9"
-        latin:keyHintLabel="("
-        latin:additionalMoreKeys="("
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_9" />
-    <Key
-        latin:keySpec="0"
-        latin:keyHintLabel=")"
-        latin:additionalMoreKeys=")"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="!text/more_keys_for_symbols_0" />
-    <!-- U+2013: "–" EN DASH
-         U+2014: "—" EM DASH
-         U+00B7: "·" MIDDLE DOT -->
-    <Key
-        latin:keySpec="-"
-        latin:keyHintLabel="_"
-        latin:additionalMoreKeys="_"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="&#x2013;,&#x2014;,&#x00B7;" />
-    <!-- U+221E: "∞" INFINITY
-         U+2260: "≠" NOT EQUAL TO
-         U+2248: "≈" ALMOST EQUAL TO -->
-    <Key
-        latin:keySpec="="
-        latin:keyHintLabel="+"
-        latin:additionalMoreKeys="+"
-        latin:keyStyle="hasShiftedLetterHintStyle"
-        latin:moreKeys="&#x221E;,&#x2260;,&#x2248;" />
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
+        >
+            <Key
+                latin:keySpec="`"
+                latin:keyHintLabel="~"
+                latin:additionalMoreKeys="~"
+                latin:keyStyle="hasShiftedLetterHintStyle" />
+            <Key
+                latin:keySpec="1"
+                latin:keyHintLabel="!"
+                latin:additionalMoreKeys="!"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_exclamation,!text/more_keys_for_symbols_1" />
+            <Key
+                latin:keySpec="2"
+                latin:keyHintLabel="\@"
+                latin:additionalMoreKeys="\@"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_2" />
+            <Key
+                latin:keySpec="3"
+                latin:keyHintLabel="\#"
+                latin:additionalMoreKeys="\#"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_3" />
+            <Key
+                latin:keySpec="4"
+                latin:keyHintLabel="$"
+                latin:additionalMoreKeys="$"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_4" />
+            <Key
+                latin:keySpec="5"
+                latin:keyHintLabel="%"
+                latin:additionalMoreKeys="\\%"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_5" />
+            <Key
+                latin:keySpec="6"
+                latin:keyHintLabel="^"
+                latin:additionalMoreKeys="^"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_6" />
+            <Key
+                latin:keySpec="7"
+                latin:keyHintLabel="&amp;"
+                latin:additionalMoreKeys="&amp;"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_7" />
+            <Key
+                latin:keySpec="8"
+                latin:keyHintLabel="*"
+                latin:additionalMoreKeys="*"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_8" />
+            <Key
+                latin:keySpec="9"
+                latin:keyHintLabel="("
+                latin:additionalMoreKeys="("
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_9" />
+            <Key
+                latin:keySpec="0"
+                latin:keyHintLabel=")"
+                latin:additionalMoreKeys=")"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_0" />
+            <!-- U+2013: "–" EN DASH
+                 U+2014: "—" EM DASH
+                 U+00B7: "·" MIDDLE DOT -->
+            <Key
+                latin:keySpec="-"
+                latin:keyHintLabel="_"
+                latin:additionalMoreKeys="_"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="&#x2013;,&#x2014;,&#x00B7;" />
+            <!-- U+221E: "∞" INFINITY
+                 U+2260: "≠" NOT EQUAL TO
+                 U+2248: "≈" ALMOST EQUAL TO -->
+            <Key
+                latin:keySpec="="
+                latin:keyHintLabel="+"
+                latin:additionalMoreKeys="+"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="&#x221E;,&#x2260;,&#x2248;" />
+        </case>
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted" -->
+        <default>
+            <include
+                 latin:keyboardLayout="@xml/rowkeys_pcqwerty1_shift" />
+        </default>
+    </switch>
 </merge>
diff --git a/java/res/xml-sw600dp/rows_pcqwerty.xml b/java/res/xml-sw600dp/rows_pcqwerty.xml
index b503d83..73b7e47 100644
--- a/java/res/xml-sw600dp/rows_pcqwerty.xml
+++ b/java/res/xml-sw600dp/rows_pcqwerty.xml
@@ -26,19 +26,8 @@
     <Row
         latin:keyWidth="7.0%p"
     >
-        <switch>
-            <case
-                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
-            >
-                <include
-                    latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
-            </case>
-            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
-            <default>
-                <include
-                     latin:keyboardLayout="@xml/rowkeys_pcqwerty1_shift" />
-            </default>
-        </switch>
+        <include
+            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml/keys_pcqwerty2_right3.xml b/java/res/xml/keys_pcqwerty2_right3.xml
index 9e62b09..b188cff 100644
--- a/java/res/xml/keys_pcqwerty2_right3.xml
+++ b/java/res/xml/keys_pcqwerty2_right3.xml
@@ -23,7 +23,7 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
         >
             <Key
                 latin:keySpec="["
diff --git a/java/res/xml/keys_pcqwerty3_right2.xml b/java/res/xml/keys_pcqwerty3_right2.xml
index d889216..8a1f60f 100644
--- a/java/res/xml/keys_pcqwerty3_right2.xml
+++ b/java/res/xml/keys_pcqwerty3_right2.xml
@@ -23,7 +23,7 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
         >
             <Key
                 latin:keySpec=";"
diff --git a/java/res/xml/keys_pcqwerty4_right3.xml b/java/res/xml/keys_pcqwerty4_right3.xml
index f32d809..6beba20 100644
--- a/java/res/xml/keys_pcqwerty4_right3.xml
+++ b/java/res/xml/keys_pcqwerty4_right3.xml
@@ -23,7 +23,7 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
         >
             <Key
                 latin:keySpec=","
diff --git a/java/res/xml/rowkeys_pcqwerty1.xml b/java/res/xml/rowkeys_pcqwerty1.xml
index fdb5072..3695733 100644
--- a/java/res/xml/rowkeys_pcqwerty1.xml
+++ b/java/res/xml/rowkeys_pcqwerty1.xml
@@ -21,61 +21,72 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <Key
-        latin:keySpec="`"
-        latin:additionalMoreKeys="~" />
-    <Key
-        latin:keySpec="1"
-        latin:additionalMoreKeys="!,!text/more_keys_for_exclamation"
-        latin:moreKeys="!text/more_keys_for_symbols_1" />
-    <Key
-        latin:keySpec="2"
-        latin:additionalMoreKeys="\@"
-        latin:moreKeys="!text/more_keys_for_symbols_2" />
-    <Key
-        latin:keySpec="3"
-        latin:additionalMoreKeys="\#"
-        latin:moreKeys="!text/more_keys_for_symbols_3" />
-    <Key
-        latin:keySpec="4"
-        latin:additionalMoreKeys="$"
-        latin:moreKeys="!text/more_keys_for_symbols_4" />
-    <Key
-        latin:keySpec="5"
-        latin:additionalMoreKeys="\\%"
-        latin:moreKeys="!text/more_keys_for_symbols_5" />
-    <Key
-        latin:keySpec="6"
-        latin:additionalMoreKeys="^"
-        latin:moreKeys="!text/more_keys_for_symbols_6" />
-    <Key
-        latin:keySpec="7"
-        latin:additionalMoreKeys="&amp;"
-        latin:moreKeys="!text/more_keys_for_symbols_7" />
-    <Key
-        latin:keySpec="8"
-        latin:additionalMoreKeys="*"
-        latin:moreKeys="!text/more_keys_for_symbols_8" />
-    <Key
-        latin:keySpec="9"
-        latin:additionalMoreKeys="("
-        latin:moreKeys="!text/more_keys_for_symbols_9" />
-    <Key
-        latin:keySpec="0"
-        latin:additionalMoreKeys=")"
-        latin:moreKeys="!text/more_keys_for_symbols_0" />
-    <!-- U+2013: "–" EN DASH
-         U+2014: "—" EM DASH
-         U+00B7: "·" MIDDLE DOT -->
-    <Key
-        latin:keySpec="-"
-        latin:additionalMoreKeys="_"
-        latin:moreKeys="&#x2013;,&#x2014;,&#x00B7;" />
-    <!-- U+221E: "∞" INFINITY
-         U+2260: "≠" NOT EQUAL TO
-         U+2248: "≈" ALMOST EQUAL TO -->
-    <Key
-        latin:keySpec="="
-        latin:additionalMoreKeys="+"
-        latin:moreKeys="!fixedColumnOrder!4,&#x221E;,&#x2260;,&#x2248;,%" />
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
+        >
+            <Key
+                latin:keySpec="`"
+                latin:additionalMoreKeys="~" />
+            <Key
+                latin:keySpec="1"
+                latin:additionalMoreKeys="!,!text/more_keys_for_exclamation"
+                latin:moreKeys="!text/more_keys_for_symbols_1" />
+            <Key
+                latin:keySpec="2"
+                latin:additionalMoreKeys="\@"
+                latin:moreKeys="!text/more_keys_for_symbols_2" />
+            <Key
+                latin:keySpec="3"
+                latin:additionalMoreKeys="\#"
+                latin:moreKeys="!text/more_keys_for_symbols_3" />
+            <Key
+                latin:keySpec="4"
+                latin:additionalMoreKeys="$"
+                latin:moreKeys="!text/more_keys_for_symbols_4" />
+            <Key
+                latin:keySpec="5"
+                latin:additionalMoreKeys="\\%"
+                latin:moreKeys="!text/more_keys_for_symbols_5" />
+            <Key
+                latin:keySpec="6"
+                latin:additionalMoreKeys="^"
+                latin:moreKeys="!text/more_keys_for_symbols_6" />
+            <Key
+                latin:keySpec="7"
+                latin:additionalMoreKeys="&amp;"
+                latin:moreKeys="!text/more_keys_for_symbols_7" />
+            <Key
+                latin:keySpec="8"
+                latin:additionalMoreKeys="*"
+                latin:moreKeys="!text/more_keys_for_symbols_8" />
+            <Key
+                latin:keySpec="9"
+                latin:additionalMoreKeys="("
+                latin:moreKeys="!text/more_keys_for_symbols_9" />
+            <Key
+                latin:keySpec="0"
+                latin:additionalMoreKeys=")"
+                latin:moreKeys="!text/more_keys_for_symbols_0" />
+            <!-- U+2013: "–" EN DASH
+                 U+2014: "—" EM DASH
+                 U+00B7: "·" MIDDLE DOT -->
+            <Key
+                latin:keySpec="-"
+                latin:additionalMoreKeys="_"
+                latin:moreKeys="&#x2013;,&#x2014;,&#x00B7;" />
+            <!-- U+221E: "∞" INFINITY
+                 U+2260: "≠" NOT EQUAL TO
+                 U+2248: "≈" ALMOST EQUAL TO -->
+            <Key
+                latin:keySpec="="
+                latin:additionalMoreKeys="+"
+                latin:moreKeys="!fixedColumnOrder!4,&#x221E;,&#x2260;,&#x2248;,%" />
+        </case>
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted" -->
+        <default>
+            <include
+                 latin:keyboardLayout="@xml/rowkeys_pcqwerty1_shift" />
+        </default>
+    </switch>
 </merge>
diff --git a/java/res/xml/rows_pcqwerty.xml b/java/res/xml/rows_pcqwerty.xml
index 8846989..a5ed745 100644
--- a/java/res/xml/rows_pcqwerty.xml
+++ b/java/res/xml/rows_pcqwerty.xml
@@ -26,19 +26,8 @@
     <Row
         latin:keyWidth="7.692%p"
     >
-        <switch>
-            <case
-                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
-            >
-                <include
-                    latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
-            </case>
-            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
-            <default>
-                <include
-                     latin:keyboardLayout="@xml/rowkeys_pcqwerty1_shift" />
-            </default>
-        </switch>
+        <include
+            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
     </Row>
     <Row
         latin:keyWidth="7.692%p"
diff --git a/java/src/com/android/inputmethod/event/Combiner.java b/java/src/com/android/inputmethod/event/Combiner.java
index ab6b70c..c3869a2 100644
--- a/java/src/com/android/inputmethod/event/Combiner.java
+++ b/java/src/com/android/inputmethod/event/Combiner.java
@@ -16,14 +16,22 @@
 
 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);
 }
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index 1deaed6..0e01c81 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -51,4 +51,19 @@
         // The dead key combiner is always active, and always first
         mCombiners.add(new DeadKeyCombiner());
     }
+
+    // Pass a new event through the whole chain.
+    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
+        final ArrayList<Event> modifiablePreviousEvents = new ArrayList<Event>(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.
+                return;
+            }
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
index ae86397..f77ce63 100644
--- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
+++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
@@ -21,14 +21,17 @@
 
 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()) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index fc5c7f7..b6d4776 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -169,7 +169,6 @@
 
         private int mDelayUpdateSuggestions;
         private int mDelayUpdateShiftState;
-        private long mDoubleSpacePeriodTimerStart;
 
         public UIHandler(final LatinIME ownerInstance) {
             super(ownerInstance);
@@ -283,10 +282,6 @@
             sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState);
         }
 
-        public void cancelUpdateShiftState() {
-            removeMessages(MSG_UPDATE_SHIFT_STATE);
-        }
-
         @UsedForTesting
         public void removeAllMessages() {
             for (int i = 0; i <= MSG_LAST; ++i) {
@@ -314,19 +309,6 @@
             obtainMessage(MSG_ON_END_BATCH_INPUT, suggestedWords).sendToTarget();
         }
 
-        public void startDoubleSpacePeriodTimer() {
-            mDoubleSpacePeriodTimerStart = SystemClock.uptimeMillis();
-        }
-
-        public void cancelDoubleSpacePeriodTimer() {
-            mDoubleSpacePeriodTimerStart = 0;
-        }
-
-        public boolean isAcceptingDoubleSpacePeriod() {
-            return SystemClock.uptimeMillis() - mDoubleSpacePeriodTimerStart
-                    < getOwnerInstance().mSettings.getCurrent().mDoubleSpacePeriodTimeout;
-        }
-
         // Working variables for the following methods.
         private boolean mIsOrientationChanging;
         private boolean mPendingSuccessiveImsCallback;
@@ -882,7 +864,6 @@
         setNeutralSuggestionStrip();
 
         mHandler.cancelUpdateSuggestionStrip();
-        mHandler.cancelDoubleSpacePeriodTimer();
 
         mainKeyboardView.setMainDictionaryAvailability(null != suggest
                 ? suggest.mDictionaryFacilitator.hasMainDictionary() : false);
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 9e5d920..29382fe 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -192,6 +192,7 @@
         final int keyX = event.mX;
         final int keyY = event.mY;
         final int newIndex = size();
+        mCombinerChain.processEvent(mEvents, event);
         mTypedWord.appendCodePoint(primaryCode);
         mEvents.add(event);
         refreshSize();
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 3c7e676..8faf175 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -95,6 +95,7 @@
     // TODO: This boolean is persistent state and causes large side effects at unexpected times.
     // Find a way to remove it for readability.
     private boolean mIsAutoCorrectionIndicatorOn;
+    private long mDoubleSpacePeriodCountdownStart;
 
     public InputLogic(final LatinIME latinIME,
             final SuggestionStripViewAccessor suggestionStripViewAccessor) {
@@ -137,6 +138,7 @@
         // In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying
         // so we try using some heuristics to find out about these and fix them.
         mConnection.tryFixLyingCursorPosition();
+        cancelDoubleSpacePeriodCountdown();
         mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
     }
 
@@ -405,7 +407,7 @@
 
         // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
         if (event.mCodePoint != Constants.CODE_SPACE) {
-            handler.cancelDoubleSpacePeriodTimer();
+            cancelDoubleSpacePeriodCountdown();
         }
 
         boolean didAutoCorrect = false;
@@ -846,7 +848,7 @@
 
         if (Constants.CODE_SPACE == codePoint) {
             if (inputTransaction.mSettingsValues.isSuggestionsRequested()) {
-                if (maybeDoubleSpacePeriod(inputTransaction.mSettingsValues, handler)) {
+                if (maybeDoubleSpacePeriod(inputTransaction)) {
                     inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
                     mSpaceState = SpaceState.DOUBLE;
                 } else if (!mSuggestedWords.isPunctuationSuggestions()) {
@@ -854,7 +856,7 @@
                 }
             }
 
-            handler.startDoubleSpacePeriodTimer();
+            startDoubleSpacePeriodCountdown(inputTransaction);
             handler.postUpdateSuggestionStrip();
         } else {
             if (swapWeakSpace) {
@@ -951,7 +953,7 @@
                 return;
             }
             if (SpaceState.DOUBLE == inputTransaction.mSpaceState) {
-                handler.cancelDoubleSpacePeriodTimer();
+                cancelDoubleSpacePeriodCountdown();
                 if (mConnection.revertDoubleSpacePeriod()) {
                     // No need to reset mSpaceState, it has already be done (that's why we
                     // receive it as a parameter)
@@ -1099,6 +1101,19 @@
         return false;
     }
 
+    public void startDoubleSpacePeriodCountdown(final InputTransaction inputTransaction) {
+        mDoubleSpacePeriodCountdownStart = inputTransaction.mTimestamp;
+    }
+
+    public void cancelDoubleSpacePeriodCountdown() {
+        mDoubleSpacePeriodCountdownStart = 0;
+    }
+
+    public boolean isDoubleSpacePeriodCountdownActive(final InputTransaction inputTransaction) {
+        return inputTransaction.mTimestamp - mDoubleSpacePeriodCountdownStart
+                < inputTransaction.mSettingsValues.mDoubleSpacePeriodTimeout;
+    }
+
     /**
      * Apply the double-space-to-period transformation if applicable.
      *
@@ -1111,14 +1126,12 @@
      * method applies the transformation and returns true. Otherwise, it does nothing and
      * returns false.
      *
-     * @param settingsValues the current values of the settings.
+     * @param inputTransaction The transaction in progress.
      * @return true if we applied the double-space-to-period transformation, false otherwise.
      */
-    private boolean maybeDoubleSpacePeriod(final SettingsValues settingsValues,
-            // TODO: remove this argument
-            final LatinIME.UIHandler handler) {
-        if (!settingsValues.mUseDoubleSpacePeriod) return false;
-        if (!handler.isAcceptingDoubleSpacePeriod()) return false;
+    private boolean maybeDoubleSpacePeriod(final InputTransaction inputTransaction) {
+        if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod) return false;
+        if (!isDoubleSpacePeriodCountdownActive(inputTransaction)) return false;
         // We only do this when we see two spaces and an accepted code point before the cursor.
         // The code point may be a surrogate pair but the two spaces may not, so we need 4 chars.
         final CharSequence lastThree = mConnection.getTextBeforeCursor(4, 0);
@@ -1134,10 +1147,10 @@
                 Character.isSurrogatePair(lastThree.charAt(0), lastThree.charAt(1)) ?
                         Character.codePointAt(lastThree, 0) : lastThree.charAt(length - 3);
         if (canBeFollowedByDoubleSpacePeriod(firstCodePoint)) {
-            handler.cancelDoubleSpacePeriodTimer();
+            cancelDoubleSpacePeriodCountdown();
             mConnection.deleteSurroundingText(2, 0);
-            final String textToInsert =
-                    settingsValues.mSpacingAndPunctuations.mSentenceSeparatorAndSpace;
+            final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations
+                    .mSentenceSeparatorAndSpace;
             mConnection.commitText(textToInsert, 1);
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert,
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index 65dad56..82b738d 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -270,9 +270,10 @@
 
     bool hasMatchedOrProximityCodePoints() const {
         // This DicNode does not have matched or proximity code points when all code points have
-        // been handled as edit corrections so far.
-        return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount()
-                < getNodeCodePointCount();
+        // been handled as edit corrections or completion so far.
+        const int editCorrectionCount = mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount();
+        const int completionCount = mDicNodeState.mDicNodeStateScoring.getCompletionCount();
+        return (editCorrectionCount + completionCount) < getNodeCodePointCount();
     }
 
     bool isTotalInputSizeExceedingLimit() const {
diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h
index 458eac8..b0db55f 100644
--- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h
+++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h
@@ -31,7 +31,7 @@
     AK_FORCE_INLINE DicNodeStateScoring()
             : mDoubleLetterLevel(NOT_A_DOUBLE_LETTER),
               mDigraphIndex(DigraphUtils::NOT_A_DIGRAPH_INDEX),
-              mEditCorrectionCount(0), mProximityCorrectionCount(0),
+              mEditCorrectionCount(0), mProximityCorrectionCount(0), mCompletionCount(0),
               mNormalizedCompoundDistance(0.0f), mSpatialDistance(0.0f), mLanguageDistance(0.0f),
               mRawLength(0.0f), mContainedErrorTypes(ErrorTypeUtils::NOT_AN_ERROR),
               mNormalizedCompoundDistanceAfterFirstWord(MAX_VALUE_FOR_WEIGHTING) {
@@ -42,6 +42,7 @@
     void init() {
         mEditCorrectionCount = 0;
         mProximityCorrectionCount = 0;
+        mCompletionCount = 0;
         mNormalizedCompoundDistance = 0.0f;
         mSpatialDistance = 0.0f;
         mLanguageDistance = 0.0f;
@@ -55,6 +56,7 @@
     AK_FORCE_INLINE void init(const DicNodeStateScoring *const scoring) {
         mEditCorrectionCount = scoring->mEditCorrectionCount;
         mProximityCorrectionCount = scoring->mProximityCorrectionCount;
+        mCompletionCount = scoring->mCompletionCount;
         mNormalizedCompoundDistance = scoring->mNormalizedCompoundDistance;
         mSpatialDistance = scoring->mSpatialDistance;
         mLanguageDistance = scoring->mLanguageDistance;
@@ -77,6 +79,9 @@
         if (ErrorTypeUtils::isProximityCorrectionError(errorType)) {
             ++mProximityCorrectionCount;
         }
+        if (ErrorTypeUtils::isCompletion(errorType)) {
+            ++mCompletionCount;
+        }
     }
 
     // Saves the current normalized distance for space-aware gestures.
@@ -129,6 +134,10 @@
         return mProximityCorrectionCount;
     }
 
+    int16_t getCompletionCount() const {
+        return mCompletionCount;
+    }
+
     float getRawLength() const {
         return mRawLength;
     }
@@ -182,6 +191,7 @@
 
     int16_t mEditCorrectionCount;
     int16_t mProximityCorrectionCount;
+    int16_t mCompletionCount;
 
     float mNormalizedCompoundDistance;
     float mSpatialDistance;
diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.h b/native/jni/src/suggest/core/dictionary/error_type_utils.h
index 1122291..cc77867 100644
--- a/native/jni/src/suggest/core/dictionary/error_type_utils.h
+++ b/native/jni/src/suggest/core/dictionary/error_type_utils.h
@@ -59,6 +59,10 @@
         return (errorType & PROXIMITY_CORRECTION) != 0;
     }
 
+    static bool isCompletion(const ErrorType errorType) {
+        return (errorType & COMPLETION) != 0;
+    }
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorTypeUtils);
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java
new file mode 100644
index 0000000..9da6dcc
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java
@@ -0,0 +1,227 @@
+/*
+ * 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.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * The PC QWERTY alphabet keyboard.
+ */
+public final class PcQwerty extends LayoutBase {
+    private static final String LAYOUT_NAME = "pcqwerty";
+
+    public PcQwerty(final LayoutCustomizer customizer) {
+        super(customizer, Symbols.class, SymbolsShifted.class);
+    }
+
+    @Override
+    public String getName() { return LAYOUT_NAME; }
+
+    public static class PcQwertyCustomizer extends LayoutCustomizer {
+        public PcQwertyCustomizer(final Locale locale) { super(locale); }
+
+        @Override
+        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+            return joinKeys(SHIFT_KEY);
+        }
+
+        @Override
+        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+            return joinKeys(SHIFT_KEY);
+        }
+
+        @Override
+        public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+            return joinKeys(SETTINGS_KEY);
+        }
+
+        @Override
+        public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+            return isPhone ? joinKeys(key(ENTER_KEY, EMOJI_KEY)) : joinKeys(EMOJI_KEY);
+        }
+    }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+        final LayoutCustomizer customizer = getCustomizer();
+        final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+        customizer.setAccentedLetters(builder);
+        builder.replaceKeyOfLabel(ROW1_1, key("`", moreKey("~")))
+                .replaceKeyOfLabel(ROW2_11, key("[", moreKey("{")))
+                .replaceKeyOfLabel(ROW2_12, key("]", moreKey("}")))
+                .replaceKeyOfLabel(ROW2_13, key("\\", moreKey("|")))
+                .replaceKeyOfLabel(ROW3_10, key(";", moreKey(":")))
+                .replaceKeyOfLabel(ROW3_11, key("'", joinMoreKeys(additionalMoreKey("\""),
+                        customizer.getDoubleQuoteMoreKeys(),
+                        customizer.getSingleQuoteMoreKeys())))
+                .setAdditionalMoreKeysPositionOf("'", 4)
+                .replaceKeyOfLabel(ROW4_8, key(",", moreKey("<")))
+                .replaceKeyOfLabel(ROW4_9, key(".", moreKey(">")))
+                // U+00BF: "¿" INVERTED QUESTION MARK
+                .replaceKeyOfLabel(ROW4_10, key("/", joinMoreKeys("?", "\u00BF")));
+        if (isPhone) {
+            // U+221E: "∞" INFINITY
+            // U+2260: "≠" NOT EQUAL TO
+            // U+2248: "≈" ALMOST EQUAL TO
+            builder.replaceKeyOfLabel(ROW1_13, key("=",
+                    joinMoreKeys("\u221E", "\u2260", "\u2248", "+")));
+        } else {
+            // U+221E: "∞" INFINITY
+            // U+2260: "≠" NOT EQUAL TO
+            // U+2248: "≈" ALMOST EQUAL TO
+            builder.replaceKeyOfLabel(ROW1_13, key("=",
+                    joinMoreKeys("+", "\u221E", "\u2260", "\u2248")));
+        }
+        return builder.build();
+    }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+        final ExpectedKeyboardBuilder builder;
+        if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED
+                || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
+            builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
+        } else {
+            builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+            final LayoutCustomizer customizer = getCustomizer();
+            customizer.setAccentedLetters(builder);
+            builder.setKeysOfRow(1,
+                    "~",
+                    // U+00A1: "¡" INVERTED EXCLAMATION MARK
+                    key("!", moreKey("\u00A1")),
+                    "@", "#",
+                    customizer.getCurrencyKey(),
+                    // U+2030: "‰" PER MILLE SIGN
+                    key("%", moreKey("\u2030")),
+                    "^", "&",
+                    // U+2020: "†" DAGGER
+                    // U+2021: "‡" DOUBLE DAGGER
+                    // U+2605: "★" BLACK STAR
+                    key("*", joinMoreKeys("\u2020", "\u2021", "\u2605")),
+                    "(", ")", "_",
+                    // U+00B1: "±" PLUS-MINUS SIGN
+                    // U+00D7: "×" MULTIPLICATION SIGN
+                    // U+00F7: "÷" DIVISION SIGN
+                    // U+221A: "√" SQUARE ROOT
+                    key("+", joinMoreKeys("\u00B1", "\u00D7", "\u00F7", "\u221A")))
+                    .replaceKeyOfLabel(ROW2_11, key("{"))
+                    .replaceKeyOfLabel(ROW2_12, key("}"))
+                    .replaceKeyOfLabel(ROW2_13, key("|"))
+                    .replaceKeyOfLabel(ROW3_10, key(":"))
+                    .replaceKeyOfLabel(ROW3_11, key("\"", joinMoreKeys(
+                            customizer.getDoubleQuoteMoreKeys(),
+                            customizer.getSingleQuoteMoreKeys())))
+                    // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+                    // U+2264: "≤" LESS-THAN OR EQUAL TO
+                    // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+                    .replaceKeyOfLabel(ROW4_8, key("<", joinMoreKeys("\u2039", "\u2264", "\u00AB")))
+                    // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+                    // U+2265: "≥" GREATER-THAN EQUAL TO
+                    // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+                    .replaceKeyOfLabel(ROW4_9, key(">", joinMoreKeys("\u203A", "\u2265", "\u00BB")))
+                    // U+00BF: "¿" INVERTED QUESTION MARK
+                    .replaceKeyOfLabel(ROW4_10, key("?", moreKey("\u00BF")));
+        }
+        builder.toUpperCase(getLocale());
+        return builder.build();
+    }
+
+    // Helper method to create alphabet layout by adding special function keys.
+    @Override
+    ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+            final boolean isPhone) {
+        final LayoutCustomizer customizer = getCustomizer();
+        builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone));
+        builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+        builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+        if (isPhone) {
+            builder.addKeysOnTheRightOfRow(3, DELETE_KEY);
+        } else {
+            builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+                    .addKeysOnTheLeftOfRow(2, TAB_KEY)
+                    .addKeysOnTheRightOfRow(3, ENTER_KEY);
+        }
+        builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone))
+                .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone));
+        return builder;
+    }
+
+    @Override
+    public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) {
+        if (elementId == KeyboardId.ELEMENT_SYMBOLS
+                || elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
+            return null;
+        }
+        return super.getLayout(isPhone, elementId);
+    }
+
+    private static final String ROW1_1 = "ROW1_1";
+    private static final String ROW1_13 = "ROW1_13";
+    private static final String ROW2_11 = "ROW2_11";
+    private static final String ROW2_12 = "ROW2_12";
+    private static final String ROW2_13 = "ROW2_13";
+    private static final String ROW3_10 = "ROW3_10";
+    private static final String ROW3_11 = "ROW3_11";
+    private static final String ROW4_8 = "ROW4_8";
+    private static final String ROW4_9 = "ROW4_9";
+    private static final String ROW4_10 = "ROW4_10";
+
+    private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+            .setKeysOfRow(1,
+                    ROW1_1,
+                    // U+00A1: "¡" INVERTED EXCLAMATION MARK
+                    // U+00B9: "¹" SUPERSCRIPT ONE
+                    // U+00BD: "½" VULGAR FRACTION ONE HALF
+                    // U+2153: "⅓" VULGAR FRACTION ONE THIRD
+                    // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
+                    // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
+                    key("1", joinMoreKeys(
+                            "!", "\u00A1", "\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B")),
+                    // U+00B2: "²" SUPERSCRIPT TWO
+                    // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
+                    key("2", joinMoreKeys("@", "\u00B2", "\u2154")),
+                    // U+00B3: "³" SUPERSCRIPT THREE
+                    // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
+                    // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
+                    key("3", joinMoreKeys("#", "\u00B3", "\u00BE", "\u215C")),
+                    // U+2074: "⁴" SUPERSCRIPT FOUR
+                    key("4", joinMoreKeys("$", "\u2074")),
+                    // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
+                    key("5", joinMoreKeys("%", "\u215D")),
+                    key("6", moreKey("^")),
+                    // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
+                    key("7", joinMoreKeys("&", "\u215E")),
+                    key("8", moreKey("*")),
+                    key("9", moreKey("(")),
+                    // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
+                    // U+2205: "∅" EMPTY SET
+                    key("0", joinMoreKeys(")", "\u207F", "\u2205")),
+                    // U+2013: "–" EN DASH
+                    // U+2014: "—" EM DASH
+                    // U+00B7: "·" MIDDLE DOT
+                    key("-", joinMoreKeys("_", "\u2013", "\u2014", "\u00B7")),
+                    ROW1_13)
+            .setKeysOfRow(2, "q", "w", "e", "r", "t", "y", "u", "i", "o", "p",
+                    ROW2_11, ROW2_12, ROW2_13)
+            .setKeysOfRow(3, "a", "s", "d", "f", "g", "h", "j", "k", "l", ROW3_10, ROW3_11)
+            .setKeysOfRow(4, "z", "x", "c", "v", "b", "n", "m", ROW4_8, ROW4_9, ROW4_10)
+            .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
index a13ec75..6176f6a 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
@@ -109,10 +109,14 @@
     // Icon ids.
     private static final int ICON_DELETE = KeyboardIconsSet.getIconId(
             KeyboardIconsSet.NAME_DELETE_KEY);
+    private static final int ICON_TAB = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_TAB_KEY);
     private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId(
             KeyboardIconsSet.NAME_SHORTCUT_KEY);
     private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId(
             KeyboardIconsSet.NAME_SETTINGS_KEY);
+    private static final int ICON_LANGUAGE_SWITCH = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY);
     private static final int ICON_ENTER = KeyboardIconsSet.getIconId(
             KeyboardIconsSet.NAME_ENTER_KEY);
     private static final int ICON_EMOJI = KeyboardIconsSet.getIconId(
@@ -120,8 +124,11 @@
 
     // Functional keys.
     public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE);
+    public static final ExpectedKey TAB_KEY = key(ICON_TAB, Constants.CODE_TAB);
     public static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT);
     public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS);
+    public static final ExpectedKey LANGUAGE_SWITCH_KEY = key(
+            ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH);
     public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER);
     public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI);
     public static final ExpectedKey SPACE_KEY = key(
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java
new file mode 100644
index 0000000..cd8d43c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java
@@ -0,0 +1,52 @@
+/*
+ * 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.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.PcQwerty;
+import com.android.inputmethod.keyboard.layout.PcQwerty.PcQwertyCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * zz: Alphabet/pcqwerty
+ */
+@SmallTest
+public final class TestsNoLanguagePcQwerty extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("zz");
+    private static final LayoutBase LAYOUT = new PcQwerty(new NoLanguagePcQwertyCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    private static class NoLanguagePcQwertyCustomizer extends PcQwertyCustomizer {
+        private final NoLanguageCustomizer mNoLanguageCustomizer;
+
+        public NoLanguagePcQwertyCustomizer(final Locale locale) {
+            super(locale);
+            mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+            return mNoLanguageCustomizer.setAccentedLetters(builder);
+        }
+    }
+}
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index 91f8af7..0e9c14e 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -36,8 +36,6 @@
 # nearly-empty implementations, for parts that we don't use in Dicttool.
 LATINIME_SRCS_FOR_DICTTOOL := \
         event/Combiner.java \
-        event/CombinerChain.java \
-        event/DeadKeyCombiner.java \
         event/Event.java \
         latin/BinaryDictionary.java \
         latin/DicTraverseSession.java \
diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
new file mode 100644
index 0000000..66ad60c
--- /dev/null
+++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
@@ -0,0 +1,26 @@
+/*
+ * 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.utils.CollectionUtils;
+
+import java.util.ArrayList;
+
+public class CombinerChain {
+    public CombinerChain(final Combiner... combinerList) {}
+    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {}
+}