Merge "Move makedict to live under dicttool (A5)" into jb-mr1-dev
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 42efb20..d467a0b 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -62,7 +62,7 @@
     <string name="gesture_input_summary" msgid="7019742443455085809">"Ingresa una palabra trazando sus letras."</string>
     <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido de gesto"</string>
     <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palabra de gesto"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar palabra de vista previa flotante al realizar gestos"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar palabra de vista previa flotante al realizar un gesto"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
     <string name="label_next_key" msgid="362972844525672568">"Siguiente"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index c89d878..9049034 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -62,7 +62,7 @@
     <string name="gesture_input_summary" msgid="7019742443455085809">"한번에 문자를 그려서 단어 입력"</string>
     <string name="gesture_preview_trail" msgid="3802333369335722221">"동작 흔적 표시"</string>
     <string name="gesture_floating_preview_text" msgid="6859416520117939680">"동작 단어 표시"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"동작과 함께 이동식 단어 미리보기 표시"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"동작에 따라 단어 미리보기 표시"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: 저장됨"</string>
     <string name="label_go_key" msgid="1635148082137219148">"이동"</string>
     <string name="label_next_key" msgid="362972844525672568">"다음"</string>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index 85bb374..4f0f19b 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -60,12 +60,9 @@
     <string name="bigram_prediction_summary" msgid="3253961591626441019">"Berdasarkan perkataan sebelumnya"</string>
     <string name="gesture_input" msgid="3310827802759290774">"Input gerak isyarat"</string>
     <string name="gesture_input_summary" msgid="7019742443455085809">"Masukkan perkataan dengan menyurih huruf perkataan itu."</string>
-    <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
-    <skip />
+    <string name="gesture_preview_trail" msgid="3802333369335722221">"Tunjukkan jejak gerak isyarat"</string>
+    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Tunjukkan perkataan gerak isyarat"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Tunjukkan perkataan pratonton terapung dengan gerak isyarat"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Disimpan"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Pergi"</string>
     <string name="label_next_key" msgid="362972844525672568">"Seterusnya"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index fcab817..c2cb4a8 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -61,8 +61,8 @@
     <string name="gesture_input" msgid="3310827802759290774">"Introd. por gestos"</string>
     <string name="gesture_input_summary" msgid="7019742443455085809">"Introduza uma palavra, desenhando as letras de uma palavra"</string>
     <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string>
-    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palavra gesto"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar palavra de visualização flutuante com gesto"</string>
+    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palavra do gesto"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar visualização flutuante da palavra com gesto"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
     <string name="label_go_key" msgid="1635148082137219148">"OK"</string>
     <string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index d4e8c09..b483067 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -60,12 +60,9 @@
     <string name="bigram_prediction_summary" msgid="3253961591626441019">"Com base na palavra anterior"</string>
     <string name="gesture_input" msgid="3310827802759290774">"Entrada por gesto"</string>
     <string name="gesture_input_summary" msgid="7019742443455085809">"Introduza uma palavra traçando suas letras"</string>
-    <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
-    <skip />
+    <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string>
+    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palavra do gesto"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar visualização flutuante da palavra com gesto"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Salvo"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
     <string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 3c522eb..d21e585 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -60,12 +60,9 @@
     <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na podlagi prejšnje besede"</string>
     <string name="gesture_input" msgid="3310827802759290774">"Vnos s potezo"</string>
     <string name="gesture_input_summary" msgid="7019742443455085809">"Vnašanje besede z drsenjem po zaslonu od črke do črke"</string>
-    <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
-    <skip />
+    <string name="gesture_preview_trail" msgid="3802333369335722221">"Prikaži pot poteze"</string>
+    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Pokaži besedo za potezo"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Pokaži plavajoči predogled besede za potezo"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: shranjeno"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Pojdi"</string>
     <string name="label_next_key" msgid="362972844525672568">"Naprej"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 4e8d2e2..4b8b45c 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -60,9 +60,9 @@
     <string name="bigram_prediction_summary" msgid="3253961591626441019">"ตามคำก่อนหน้า"</string>
     <string name="gesture_input" msgid="3310827802759290774">"ป้อนท่าทางสัมผัส"</string>
     <string name="gesture_input_summary" msgid="7019742443455085809">"ป้อนคำโดยลากนิ้วเป็นรูปตัวอักษรที่อยู่ในคำ โดยไม่ยกนิ้วขึ้น"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"แสดงรอยทางเดินของท่าทาง"</string>
-    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"แสดงคำแนะนำท่าทาง"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"แสดงตัวอย่างคำแนะนำพร้อมด้วยท่าทาง"</string>
+    <string name="gesture_preview_trail" msgid="3802333369335722221">"แสดงรอยทางเดินของท่าทางสัมผัส"</string>
+    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"แสดงคำแนะนำท่าทางสัมผัส"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"แสดงตัวอย่างคำแนะนำพร้อมด้วยท่าทางสัมผัส"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : บันทึกแล้ว"</string>
     <string name="label_go_key" msgid="1635148082137219148">"ไป"</string>
     <string name="label_next_key" msgid="362972844525672568">"ถัดไป"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 0014db3..d8fec34 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -60,12 +60,9 @@
     <string name="bigram_prediction_summary" msgid="3253961591626441019">"根据上一个字词提供建议"</string>
     <string name="gesture_input" msgid="3310827802759290774">"手势输入"</string>
     <string name="gesture_input_summary" msgid="7019742443455085809">"连笔书写输入字词"</string>
-    <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
-    <skip />
-    <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
-    <skip />
+    <string name="gesture_preview_trail" msgid="3802333369335722221">"显示手势轨迹"</string>
+    <string name="gesture_floating_preview_text" msgid="6859416520117939680">"显示手势文字"</string>
+    <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"手势划动输入时显示漂浮预览文字"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string>
     <string name="label_go_key" msgid="1635148082137219148">"开始"</string>
     <string name="label_next_key" msgid="362972844525672568">"下一步"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 02e8eeb..4975d65 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -119,6 +119,7 @@
             <enum name="boldItalic" value="3" />
         </attr>
 
+        <!-- Attributes for PreviewPlacerView -->
         <attr name="gestureFloatingPreviewTextSize" format="dimension" />
         <attr name="gestureFloatingPreviewTextColor" format="color" />
         <attr name="gestureFloatingPreviewTextOffset" format="dimension" />
@@ -128,6 +129,8 @@
         <attr name="gestureFloatingPreviewTextShadowBorder" format="dimension" />
         <attr name="gestureFloatingPreviewTextConnectorColor" format="color" />
         <attr name="gestureFloatingPreviewTextConnectorWidth" format="dimension" />
+        <!-- Delay after gesture input and gesture floating preview text dismissing in millisecond -->
+        <attr name="gestureFloatingPreviewTextLingerTimeout" format="integer" />
         <attr name="gesturePreviewTrailColor" format="color" />
         <attr name="gesturePreviewTrailWidth" format="dimension" />
     </declare-styleable>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index e5575e7..54a6687 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -49,6 +49,7 @@
          Configuration for KeyboardView
     -->
     <integer name="config_key_preview_linger_timeout">70</integer>
+    <integer name="config_gesture_floating_preview_text_linger_timeout">200</integer>
     <!--
          Configuration for MainKeyboardView
     -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 955a276..ae67c43 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -77,6 +77,7 @@
         <item name="gestureFloatingPreviewTextShadowBorder">@dimen/gesture_floating_preview_text_shadow_border</item>
         <item name="gestureFloatingPreviewTextConnectorColor">@android:color/white</item>
         <item name="gestureFloatingPreviewTextConnectorWidth">@dimen/gesture_floating_preview_text_connector_width</item>
+        <item name="gestureFloatingPreviewTextLingerTimeout">@integer/config_gesture_floating_preview_text_linger_timeout</item>
         <item name="gesturePreviewTrailColor">@android:color/holo_blue_light</item>
         <item name="gesturePreviewTrailWidth">@dimen/gesture_preview_trail_width</item>
         <!-- Common attributes of MainKeyboardView -->
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index a0f48d2..1183b5f 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -120,7 +120,6 @@
             spannable = new SpannableString(pickedWord);
         }
         final ArrayList<String> suggestionsList = new ArrayList<String>();
-        boolean sameAsTyped = false;
         for (int i = 0; i < suggestedWords.size(); ++i) {
             if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) {
                 break;
@@ -128,8 +127,6 @@
             final CharSequence word = suggestedWords.getWord(i);
             if (!TextUtils.equals(pickedWord, word)) {
                 suggestionsList.add(word.toString());
-            } else if (i == 0) {
-                sameAsTyped = true;
             }
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index a3b7776..97d88af 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -37,8 +37,9 @@
     }
 
     public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
-        if (keyboard == null)
+        if (keyboard == null) {
             throw new NullPointerException();
+        }
         mCorrectionX = (int)correctionX;
         mCorrectionY = (int)correctionY;
         mKeyboard = keyboard;
@@ -58,8 +59,6 @@
     }
 
     public Keyboard getKeyboard() {
-        if (mKeyboard == null)
-            throw new IllegalStateException("keyboard isn't set");
         return mKeyboard;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index dc84763..10f651a 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -267,7 +267,7 @@
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void startDoubleTapTimer() {
-        final MainKeyboardView keyboardView = getKeyboardView();
+        final MainKeyboardView keyboardView = getMainKeyboardView();
         if (keyboardView != null) {
             final TimerProxy timer = keyboardView.getTimerProxy();
             timer.startDoubleTapTimer();
@@ -277,7 +277,7 @@
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void cancelDoubleTapTimer() {
-        final MainKeyboardView keyboardView = getKeyboardView();
+        final MainKeyboardView keyboardView = getMainKeyboardView();
         if (keyboardView != null) {
             final TimerProxy timer = keyboardView.getTimerProxy();
             timer.cancelDoubleTapTimer();
@@ -287,7 +287,7 @@
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public boolean isInDoubleTapTimeout() {
-        final MainKeyboardView keyboardView = getKeyboardView();
+        final MainKeyboardView keyboardView = getMainKeyboardView();
         return (keyboardView != null)
                 ? keyboardView.getTimerProxy().isInDoubleTapTimeout() : false;
     }
@@ -295,7 +295,7 @@
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void startLongPressTimer(int code) {
-        final MainKeyboardView keyboardView = getKeyboardView();
+        final MainKeyboardView keyboardView = getMainKeyboardView();
         if (keyboardView != null) {
             final TimerProxy timer = keyboardView.getTimerProxy();
             timer.startLongPressTimer(code);
@@ -305,7 +305,7 @@
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void cancelLongPressTimer() {
-        final MainKeyboardView keyboardView = getKeyboardView();
+        final MainKeyboardView keyboardView = getMainKeyboardView();
         if (keyboardView != null) {
             final TimerProxy timer = keyboardView.getTimerProxy();
             timer.cancelLongPressTimer();
@@ -345,7 +345,7 @@
         mState.onCodeInput(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
     }
 
-    public MainKeyboardView getKeyboardView() {
+    public MainKeyboardView getMainKeyboardView() {
         return mKeyboardView;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 69e4d98..0e6de70 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -140,7 +140,7 @@
     private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
 
     public static class DrawingHandler extends StaticInnerHandlerWrapper<KeyboardView> {
-        private static final int MSG_DISMISS_KEY_PREVIEW = 1;
+        private static final int MSG_DISMISS_KEY_PREVIEW = 0;
 
         public DrawingHandler(KeyboardView outerInstance) {
             super(outerInstance);
@@ -902,6 +902,9 @@
 
     public void cancelAllMessages() {
         mDrawingHandler.cancelAllMessages();
+        if (mPreviewPlacerView != null) {
+            mPreviewPlacerView.cancelAllMessages();
+        }
     }
 
     // Called by {@link PointerTracker} constructor to create a TextView.
@@ -943,6 +946,11 @@
         mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText);
     }
 
+    public void dismissGestureFloatingPreviewText() {
+        locatePreviewPlacerView();
+        mPreviewPlacerView.dismissGestureFloatingPreviewText();
+    }
+
     @Override
     public void showGestureTrail(PointerTracker tracker) {
         locatePreviewPlacerView();
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 7945908..fe9cb94 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -122,10 +122,10 @@
 
     private static class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
             implements TimerProxy {
+        private static final int MSG_TYPING_STATE_EXPIRED = 0;
         private static final int MSG_REPEAT_KEY = 1;
         private static final int MSG_LONGPRESS_KEY = 2;
         private static final int MSG_DOUBLE_TAP = 3;
-        private static final int MSG_TYPING_STATE_EXPIRED = 4;
 
         private final KeyTimerParams mParams;
 
@@ -139,6 +139,9 @@
             final MainKeyboardView keyboardView = getOuterInstance();
             final PointerTracker tracker = (PointerTracker) msg.obj;
             switch (msg.what) {
+            case MSG_TYPING_STATE_EXPIRED:
+                startWhileTypingFadeinAnimation(keyboardView);
+                break;
             case MSG_REPEAT_KEY:
                 final Key currentKey = tracker.getKey();
                 if (currentKey != null && currentKey.mCode == msg.arg1) {
@@ -153,9 +156,6 @@
                     KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
                 }
                 break;
-            case MSG_TYPING_STATE_EXPIRED:
-                startWhileTypingFadeinAnimation(keyboardView);
-                break;
             }
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index cd4e300..a183546 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -39,7 +39,11 @@
 
         Key nearestKey = null;
         int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
-        for (final Key key : getKeyboard().mKeys) {
+        final Keyboard keyboard = getKeyboard();
+        if (keyboard == null) {
+            throw new NullPointerException("Keyboard isn't set");
+        }
+        for (final Key key : keyboard.mKeys) {
             final int dist = key.squaredDistanceToEdge(touchX, touchY);
             if (dist < nearestDist) {
                 nearestKey = key;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index e7e11f4..184011f 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -34,7 +34,7 @@
 
 import java.util.ArrayList;
 
-public class PointerTracker {
+public class PointerTracker implements PointerTrackerQueue.ElementActions {
     private static final String TAG = PointerTracker.class.getSimpleName();
     private static final boolean DEBUG_EVENT = false;
     private static final boolean DEBUG_MOVE_EVENT = false;
@@ -414,6 +414,7 @@
         mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
     }
 
+    @Override
     public boolean isInSlidingKeyInput() {
         return mIsInSlidingKeyInput;
     }
@@ -422,6 +423,7 @@
         return mCurrentKey;
     }
 
+    @Override
     public boolean isModifier() {
         return mCurrentKey != null && mCurrentKey.isModifier();
     }
@@ -805,7 +807,7 @@
                         if (ProductionFlag.IS_EXPERIMENTAL) {
                             ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
                         }
-                        onUpEventInternal(x, y, eventTime);
+                        onUpEventInternal();
                         onDownEventInternal(x, y, eventTime);
                     } else {
                         // HACK: If there are currently multiple touches, register the key even if
@@ -815,7 +817,7 @@
                         // this hack.
                         if (me != null && me.getPointerCount() > 1
                                 && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
-                            onUpEventInternal(x, y, eventTime);
+                            onUpEventInternal();
                         }
                         if (!mIsPossibleGesture) {
                             mKeyAlreadyProcessed = true;
@@ -860,20 +862,21 @@
             }
             queue.remove(this);
         }
-        onUpEventInternal(x, y, eventTime);
+        onUpEventInternal();
     }
 
     // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
     // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
     // "virtual" up event.
-    public void onPhantomUpEvent(int x, int y, long eventTime) {
+    @Override
+    public void onPhantomUpEvent(long eventTime) {
         if (DEBUG_EVENT)
-            printTouchEvent("onPhntEvent:", x, y, eventTime);
-        onUpEventInternal(x, y, eventTime);
+            printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
+        onUpEventInternal();
         mKeyAlreadyProcessed = true;
     }
 
-    private void onUpEventInternal(int x, int y, long eventTime) {
+    private void onUpEventInternal() {
         mTimerProxy.cancelKeyTimers();
         mIsInSlidingKeyInput = false;
         mIsPossibleGesture = false;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index e4a7184..bd16480 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -18,9 +18,6 @@
 
 import android.util.Log;
 
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.PointerTracker;
-
 import java.util.Iterator;
 import java.util.LinkedList;
 
@@ -28,37 +25,43 @@
     private static final String TAG = PointerTrackerQueue.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    public interface ElementActions {
+        public boolean isModifier();
+        public boolean isInSlidingKeyInput();
+        public void onPhantomUpEvent(long eventTime);
+    }
+
     // TODO: Use ring buffer instead of {@link LinkedList}.
-    private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
+    private final LinkedList<ElementActions> mQueue = new LinkedList<ElementActions>();
 
     public int size() {
         return mQueue.size();
     }
 
-    public synchronized void add(PointerTracker tracker) {
+    public synchronized void add(ElementActions tracker) {
         mQueue.add(tracker);
     }
 
-    public synchronized void remove(PointerTracker tracker) {
+    public synchronized void remove(ElementActions tracker) {
         mQueue.remove(tracker);
     }
 
-    public synchronized void releaseAllPointersOlderThan(PointerTracker tracker,
+    public synchronized void releaseAllPointersOlderThan(ElementActions tracker,
             long eventTime) {
         if (DEBUG) {
-            Log.d(TAG, "releaseAllPoniterOlderThan: [" + tracker.mPointerId + "] " + this);
+            Log.d(TAG, "releaseAllPoniterOlderThan: " + tracker + " " + this);
         }
         if (!mQueue.contains(tracker)) {
             return;
         }
-        final Iterator<PointerTracker> it = mQueue.iterator();
+        final Iterator<ElementActions> it = mQueue.iterator();
         while (it.hasNext()) {
-            final PointerTracker t = it.next();
+            final ElementActions t = it.next();
             if (t == tracker) {
                 break;
             }
             if (!t.isModifier()) {
-                t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
+                t.onPhantomUpEvent(eventTime);
                 it.remove();
             }
         }
@@ -68,28 +71,28 @@
         releaseAllPointersExcept(null, eventTime);
     }
 
-    public synchronized void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
+    public synchronized void releaseAllPointersExcept(ElementActions tracker, long eventTime) {
         if (DEBUG) {
             if (tracker == null) {
                 Log.d(TAG, "releaseAllPoniters: " + this);
             } else {
-                Log.d(TAG, "releaseAllPoniterExcept: [" + tracker.mPointerId + "] " + this);
+                Log.d(TAG, "releaseAllPoniterExcept: " + tracker + " " + this);
             }
         }
-        final Iterator<PointerTracker> it = mQueue.iterator();
+        final Iterator<ElementActions> it = mQueue.iterator();
         while (it.hasNext()) {
-            final PointerTracker t = it.next();
+            final ElementActions t = it.next();
             if (t != tracker) {
-                t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
+                t.onPhantomUpEvent(eventTime);
                 it.remove();
             }
         }
     }
 
-    public synchronized boolean hasModifierKeyOlderThan(PointerTracker tracker) {
-        final Iterator<PointerTracker> it = mQueue.iterator();
+    public synchronized boolean hasModifierKeyOlderThan(ElementActions tracker) {
+        final Iterator<ElementActions> it = mQueue.iterator();
         while (it.hasNext()) {
-            final PointerTracker t = it.next();
+            final ElementActions t = it.next();
             if (t == tracker) {
                 break;
             }
@@ -101,7 +104,7 @@
     }
 
     public synchronized boolean isAnyInSlidingKeyInput() {
-        for (final PointerTracker tracker : mQueue) {
+        for (final ElementActions tracker : mQueue) {
             if (tracker.isInSlidingKeyInput()) {
                 return true;
             }
@@ -112,12 +115,11 @@
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
-        for (final PointerTracker tracker : mQueue) {
+        for (final ElementActions tracker : mQueue) {
             if (sb.length() > 0)
                 sb.append(" ");
-            sb.append("[" + tracker.mPointerId + " "
-                + Keyboard.printableCode(tracker.getKey().mCode) + "]");
+            sb.append(tracker.toString());
         }
-        return sb.toString();
+        return "[" + sb.toString() + "]";
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
index c38febf..d0fecf0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -21,12 +21,14 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Paint.Align;
+import android.os.Message;
 import android.text.TextUtils;
 import android.util.SparseArray;
 import android.widget.RelativeLayout;
 
 import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 
 public class PreviewPlacerView extends RelativeLayout {
     private final Paint mGesturePaint;
@@ -39,6 +41,7 @@
     private final int mGestureFloatingPreviewTextShadingBorder;
     private final int mGestureFloatingPreviewTextConnectorColor;
     private final int mGestureFloatingPreviewTextConnectorWidth;
+    /* package */ final int mGestureFloatingPreviewTextLingerTimeout;
 
     private int mXOrigin;
     private int mYOrigin;
@@ -49,6 +52,43 @@
     private boolean mDrawsGesturePreviewTrail;
     private boolean mDrawsGestureFloatingPreviewText;
 
+    private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
+
+    private static class DrawingHandler extends StaticInnerHandlerWrapper<PreviewPlacerView> {
+        private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 0;
+
+        public DrawingHandler(PreviewPlacerView outerInstance) {
+            super(outerInstance);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final PreviewPlacerView placerView = getOuterInstance();
+            if (placerView == null) return;
+            switch (msg.what) {
+            case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
+                placerView.setGestureFloatingPreviewText(null);
+                break;
+            }
+        }
+
+        private void cancelDismissGestureFloatingPreviewText() {
+            removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
+        }
+
+        public void dismissGestureFloatingPreviewText() {
+            cancelDismissGestureFloatingPreviewText();
+            final PreviewPlacerView placerView = getOuterInstance();
+            sendMessageDelayed(
+                    obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT),
+                    placerView.mGestureFloatingPreviewTextLingerTimeout);
+        }
+
+        public void cancelAllMessages() {
+            cancelDismissGestureFloatingPreviewText();
+        }
+    }
+
     public PreviewPlacerView(Context context, TypedArray keyboardViewAttr) {
         super(context);
         setWillNotDraw(false);
@@ -71,6 +111,8 @@
                 R.styleable.KeyboardView_gestureFloatingPreviewTextConnectorColor, 0);
         mGestureFloatingPreviewTextConnectorWidth = keyboardViewAttr.getDimensionPixelSize(
                 R.styleable.KeyboardView_gestureFloatingPreviewTextConnectorWidth, 0);
+        mGestureFloatingPreviewTextLingerTimeout = keyboardViewAttr.getInt(
+                R.styleable.KeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
         final int gesturePreviewTrailColor = keyboardViewAttr.getColor(
                 R.styleable.KeyboardView_gesturePreviewTrailColor, 0);
         final int gesturePreviewTrailWidth = keyboardViewAttr.getDimensionPixelSize(
@@ -136,6 +178,14 @@
         invalidate();
     }
 
+    public void dismissGestureFloatingPreviewText() {
+        mDrawingHandler.dismissGestureFloatingPreviewText();
+    }
+
+    public void cancelAllMessages() {
+        mDrawingHandler.cancelAllMessages();
+    }
+
     private void drawGestureFloatingPreviewText(Canvas canvas, PointerTracker tracker,
             String gestureFloatingPreviewText) {
         if (TextUtils.isEmpty(gestureFloatingPreviewText)) {
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index fd40aa6..60fe17b 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -16,8 +16,6 @@
 
 package com.android.inputmethod.latin;
 
-import android.text.TextUtils;
-
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 88ac07d..ee80f25 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -65,7 +65,7 @@
                 prevWord, proximityInfo);
         if (null == suggestions) suggestions = new ArrayList<SuggestedWordInfo>();
         final int length = dictionaries.size();
-        for (int i = 0; i < length; ++ i) {
+        for (int i = 1; i < length; ++ i) {
             final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
                     prevWord, proximityInfo);
             if (null != sugg) suggestions.addAll(sugg);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index f8a6fc8..4550860 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -181,9 +181,9 @@
     public final UIHandler mHandler = new UIHandler(this);
 
     public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
-        private static final int MSG_UPDATE_SHIFT_STATE = 1;
-        private static final int MSG_PENDING_IMS_CALLBACK = 6;
-        private static final int MSG_UPDATE_SUGGESTION_STRIP = 7;
+        private static final int MSG_UPDATE_SHIFT_STATE = 0;
+        private static final int MSG_PENDING_IMS_CALLBACK = 1;
+        private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
 
         private int mDelayUpdateSuggestions;
         private int mDelayUpdateShiftState;
@@ -545,8 +545,9 @@
             commitTyped(LastComposedWord.NOT_A_SEPARATOR);
             mConnection.finishComposingText();
             mConnection.endBatchEdit();
-            if (isShowingOptionDialog())
+            if (isShowingOptionDialog()) {
                 mOptionsDialog.dismiss();
+            }
         }
         super.onConfigurationChanged(conf);
     }
@@ -612,7 +613,7 @@
     private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
         super.onStartInputView(editorInfo, restarting);
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
-        MainKeyboardView inputView = switcher.getKeyboardView();
+        final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
 
         if (editorInfo == null) {
             Log.e(TAG, "Null EditorInfo in onStartInputView()");
@@ -655,7 +656,7 @@
 
         LatinImeLogger.onStartInputView(editorInfo);
         // In landscape mode, this method gets called without the input view being created.
-        if (inputView == null) {
+        if (mainKeyboardView == null) {
             return;
         }
 
@@ -692,7 +693,7 @@
         }
 
         if (!restarting) {
-            inputView.closing();
+            mainKeyboardView.closing();
             loadSettings();
 
             if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) {
@@ -718,7 +719,7 @@
         mHandler.cancelUpdateSuggestionStrip();
         mHandler.cancelDoubleSpacesTimer();
 
-        inputView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
+        mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
                 mCurrentSettings.mKeyPreviewPopupDismissDelay);
 
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
@@ -737,8 +738,10 @@
                     getCurrentInputConnection());
         }
         super.onWindowHidden();
-        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
-        if (inputView != null) inputView.closing();
+        final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView != null) {
+            mainKeyboardView.closing();
+        }
     }
 
     private void onFinishInputInternal() {
@@ -749,15 +752,19 @@
             ResearchLogger.getInstance().latinIME_onFinishInputInternal();
         }
 
-        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
-        if (inputView != null) inputView.closing();
+        final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView != null) {
+            mainKeyboardView.closing();
+        }
     }
 
     private void onFinishInputViewInternal(boolean finishingInput) {
         super.onFinishInputView(finishingInput);
         mKeyboardSwitcher.onFinishInputView();
-        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
-        if (inputView != null) inputView.cancelAllMessages();
+        final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView != null) {
+            mainKeyboardView.cancelAllMessages();
+        }
         // Remove pending messages related to update suggestions
         mHandler.cancelUpdateSuggestionStrip();
     }
@@ -924,8 +931,9 @@
     private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
         // TODO: Modify this if we support suggestions with hard keyboard
         if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
-            final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
-            final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
+            final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+            final boolean inputViewShown = (mainKeyboardView != null)
+                    ? mainKeyboardView.isShown() : false;
             final boolean shouldShowSuggestions = shown
                     && (needsInputViewShown ? inputViewShown : true);
             if (isFullscreenMode()) {
@@ -948,11 +956,11 @@
             return currentHeight;
         }
 
-        final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
-        if (keyboardView == null) {
+        final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView == null) {
             return 0;
         }
-        final int keyboardHeight = keyboardView.getHeight();
+        final int keyboardHeight = mainKeyboardView.getHeight();
         final int suggestionsHeight = mSuggestionsContainer.getHeight();
         final int displayHeight = mResources.getDisplayMetrics().heightPixels;
         final Rect rect = new Rect();
@@ -970,9 +978,10 @@
     @Override
     public void onComputeInsets(InputMethodService.Insets outInsets) {
         super.onComputeInsets(outInsets);
-        final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
-        if (inputView == null || mSuggestionsContainer == null)
+        final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView == null || mSuggestionsContainer == null) {
             return;
+        }
         final int adjustedBackingHeight = getAdjustedBackingViewHeight();
         final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE);
         final int backingHeight = backingGone ? 0 : adjustedBackingHeight;
@@ -985,13 +994,12 @@
         final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
         int touchY = extraHeight;
         // Need to set touchable region only if input view is being shown
-        final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
-        if (keyboardView != null && keyboardView.isShown()) {
+        if (mainKeyboardView.isShown()) {
             if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
                 touchY -= suggestionsHeight;
             }
-            final int touchWidth = inputView.getWidth();
-            final int touchHeight = inputView.getHeight() + extraHeight
+            final int touchWidth = mainKeyboardView.getWidth();
+            final int touchHeight = mainKeyboardView.getHeight() + extraHeight
                     // Extend touchable region below the keyboard.
                     + EXTENDED_TOUCHABLE_REGION_HEIGHT;
             outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
@@ -1046,7 +1054,6 @@
                     LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
                     separatorCode, prevWord);
         }
-        updateSuggestionStrip();
     }
 
     // Called from the KeyboardSwitcher which needs to know auto caps state to display
@@ -1330,10 +1337,11 @@
 
     // Called from PointerTracker through the KeyboardActionListener interface
     @Override
-    public void onTextInput(CharSequence text) {
+    public void onTextInput(CharSequence rawText) {
         mConnection.beginBatchEdit();
         commitTyped(LastComposedWord.NOT_A_SEPARATOR);
-        text = specificTldProcessingOnTextInput(text);
+        mHandler.postUpdateSuggestionStrip();
+        final CharSequence text = specificTldProcessingOnTextInput(rawText);
         if (SPACE_STATE_PHANTOM == mSpaceState) {
             sendKeyCodePoint(Keyboard.CODE_SPACE);
         }
@@ -1371,7 +1379,7 @@
         showSuggestionStrip(suggestedWords, null);
         final String gestureFloatingPreviewText = (suggestedWords.size() > 0)
                 ? suggestedWords.getWord(0) : null;
-        mKeyboardSwitcher.getKeyboardView()
+        mKeyboardSwitcher.getMainKeyboardView()
                 .showGestureFloatingPreviewText(gestureFloatingPreviewText);
     }
 
@@ -1380,7 +1388,11 @@
         mWordComposer.setBatchInputPointers(batchPointers);
         final SuggestedWords suggestedWords = getSuggestedWords();
         showSuggestionStrip(suggestedWords, null);
-        mKeyboardSwitcher.getKeyboardView().showGestureFloatingPreviewText(null);
+        final String gestureFloatingPreviewText = (suggestedWords.size() > 0)
+                ? suggestedWords.getWord(0) : null;
+        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        mainKeyboardView.showGestureFloatingPreviewText(gestureFloatingPreviewText);
+        mainKeyboardView.dismissGestureFloatingPreviewText();
         if (suggestedWords == null || suggestedWords.size() == 0) {
             return;
         }
@@ -1585,7 +1597,7 @@
                 keyY = y;
             } else {
                 final KeyDetector keyDetector =
-                        mKeyboardSwitcher.getKeyboardView().getKeyDetector();
+                        mKeyboardSwitcher.getMainKeyboardView().getKeyDetector();
                 keyX = keyDetector.getTouchX(x);
                 keyY = keyDetector.getTouchY(y);
             }
@@ -1687,9 +1699,10 @@
     private void handleClose() {
         commitTyped(LastComposedWord.NOT_A_SEPARATOR);
         requestHideSelf(0);
-        MainKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
-        if (inputView != null)
-            inputView.closing();
+        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView != null) {
+            mainKeyboardView.closing();
+        }
     }
 
     // TODO: make this private
@@ -2087,7 +2100,7 @@
         // onConfigurationChanged before SoftInputWindow is shown.
         initSuggest();
         loadSettings();
-        if (mKeyboardSwitcher.getKeyboardView() != null) {
+        if (mKeyboardSwitcher.getMainKeyboardView() != null) {
             // Reload keyboard because the current language has been changed.
             mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
             updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability();
@@ -2099,11 +2112,11 @@
     }
 
     private void updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability() {
-        final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
-        if (keyboardView != null) {
+        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView != null) {
             final boolean shouldHandleGesture = mCurrentSettings.mGestureInputEnabled
                     && mIsMainDictionaryAvailable;
-            keyboardView.setGestureHandlingMode(shouldHandleGesture,
+            mainKeyboardView.setGestureHandlingMode(shouldHandleGesture,
                     mCurrentSettings.mGesturePreviewTrailEnabled,
                     mCurrentSettings.mGestureFloatingPreviewTextEnabled);
         }
@@ -2112,7 +2125,8 @@
     // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
     // {@link KeyboardSwitcher}. Called from KeyboardSwitcher
     public void hapticAndAudioFeedback(final int primaryCode) {
-        mFeedbackManager.hapticAndAudioFeedback(primaryCode, mKeyboardSwitcher.getKeyboardView());
+        mFeedbackManager.hapticAndAudioFeedback(
+                primaryCode, mKeyboardSwitcher.getMainKeyboardView());
     }
 
     // Callback called by PointerTracker through the KeyboardActionListener. This is called when a
@@ -2223,8 +2237,10 @@
     }
 
     public void showOptionDialog(AlertDialog dialog) {
-        final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
-        if (windowToken == null) return;
+        final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
+        if (windowToken == null) {
+            return;
+        }
 
         dialog.setCancelable(true);
         dialog.setCanceledOnTouchOutside(true);
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 8f71de0..c6b5c33 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -44,7 +44,6 @@
 import java.io.PrintWriter;
 import java.nio.channels.FileChannel;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
 
@@ -220,7 +219,7 @@
     }
 
     public static String getStackTrace() {
-        return getStackTrace(Integer.MAX_VALUE);
+        return getStackTrace(Integer.MAX_VALUE - 1);
     }
 
     public static class UsabilityStudyLogUtils {
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 68bd98a..cf6f31a 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -209,7 +209,7 @@
         if (mSplashDialog != null && mSplashDialog.isShowing()) {
             return;
         }
-        final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
+        final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
         if (windowToken == null) {
             return;
         }
@@ -540,11 +540,11 @@
         if (mKeyboardSwitcher == null) {
             return;
         }
-        final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
-        if (keyboardView == null) {
+        final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+        if (mainKeyboardView == null) {
             return;
         }
-        keyboardView.invalidateAllKeys();
+        mainKeyboardView.invalidateAllKeys();
     }
 
 
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 2b30f3c..31dd61e 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -25,17 +25,52 @@
 #define AKLOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__)
 #define AKLOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__)
 
+#define DUMP_RESULT(words, frequencies, maxWordCount, maxWordLength) do { \
+        dumpResult(words, frequencies, maxWordCount, maxWordLength); } while(0)
 #define DUMP_WORD(word, length) do { dumpWord(word, length); } while(0)
 #define DUMP_WORD_INT(word, length) do { dumpWordInt(word, length); } while(0)
 
+static inline void dumpWordInfo(const unsigned short *word, const int length,
+        const int rank, const int frequency) {
+    static char charBuf[50];
+    int i = 0;
+    for (; i < length; ++i) {
+        const unsigned short c = word[i];
+        if (c == 0) {
+            break;
+        }
+        charBuf[i] = c;
+    }
+    charBuf[i] = 0;
+    if (i > 1) {
+        AKLOGI("%2d [ %s ] (%d)", rank, charBuf, frequency);
+    }
+}
+
+static inline void dumpResult(
+        const unsigned short *outWords, const int *frequencies, const int maxWordCounts,
+        const int maxWordLength) {
+    AKLOGI("--- DUMP RESULT ---------");
+    for (int i = 0; i < maxWordCounts; ++i) {
+        dumpWordInfo(&outWords[i * maxWordLength], maxWordLength, i, frequencies[i]);
+    }
+    AKLOGI("-------------------------");
+}
+
 static inline void dumpWord(const unsigned short *word, const int length) {
     static char charBuf[50];
-
-    for (int i = 0; i < length; ++i) {
-        charBuf[i] = word[i];
+    int i = 0;
+    for (; i < length; ++i) {
+        const unsigned short c = word[i];
+        if (c == 0) {
+            break;
+        }
+        charBuf[i] = c;
     }
-    charBuf[length] = 0;
-    AKLOGI("[ %s ]", charBuf);
+    charBuf[i] = 0;
+    if (i > 1) {
+        AKLOGI("[ %s ]", charBuf);
+    }
 }
 
 static inline void dumpWordInt(const int *word, const int length) {
@@ -51,6 +86,7 @@
 #else
 #define AKLOGE(fmt, ...)
 #define AKLOGI(fmt, ...)
+#define DUMP_RESULT(words, frequencies, maxWordCount, maxWordLength)
 #define DUMP_WORD(word, length)
 #define DUMP_WORD_INT(word, length)
 #endif
diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp
index 6c72211..ee55cfa 100644
--- a/native/jni/src/dictionary.cpp
+++ b/native/jni/src/dictionary.cpp
@@ -67,6 +67,9 @@
         result = mGestureDecoder->getSuggestions(proximityInfo, xcoordinates, ycoordinates,
                 times, pointerIds, codes, codesSize, commitPoint,
                 outWords, frequencies, spaceIndices, outputTypes);
+        if (DEBUG_DICT) {
+            DUMP_RESULT(outWords, frequencies, 18 /* MAX_WORDS */, MAX_WORD_LENGTH_INTERNAL);
+        }
         return result;
     } else {
         std::map<int, int> bigramMap;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
index 64cf7a6..f5ad723 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
@@ -420,38 +420,38 @@
 
     public void testDoubleTapShiftAndChording() {
         // TODO: The following tests fail due to bug. Temporarily commented.
-        // First shift key tap.
-        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
-        // Second shift key tap, maybe shift locked.
-        secondPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
-        // Press/release letter key, remain in manual shifted.
-        chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
-        // Release shift key, back to alphabet shifted (not shift locked).
-        releaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
-
-        // Long press shift key, enter alphabet shift locked.
-        longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED,
-                ALPHABET_SHIFT_LOCKED);
-        // First shift key tap.
-        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
-        // Second shift key tap, maybe shift unlocked.
-        secondPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
-        // Press/release letter key, remain in manual shifted.
-        chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
-        // Release shift key, back to alphabet (not shift locked).
-        releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED);
-
-        // Set capitalize the first character of all words mode.
-        setAutoCapsMode(CAP_MODE_WORDS);
-        // Load keyboard, should be in automatic shifted.
-        loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED);
-        // First shift key tap.
-        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
-        // Second shift key tap, maybe shift locked.
-        secondPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
-        // Press/release letter key, remain in manual shifted.
-        chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
-        // Release shift key, back to alphabet (not shift locked).
-        releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED);
+//        // First shift key tap.
+//        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+//        // Second shift key tap, maybe shift locked.
+//        secondPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
+//        // Press/release letter key, remain in manual shifted.
+//        chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+//        // Release shift key, back to alphabet shifted (not shift locked).
+//        releaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
+//
+//        // Long press shift key, enter alphabet shift locked.
+//        longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED,
+//                ALPHABET_SHIFT_LOCKED);
+//        // First shift key tap.
+//        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+//        // Second shift key tap, maybe shift unlocked.
+//        secondPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
+//        // Press/release letter key, remain in manual shifted.
+//        chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+//        // Release shift key, back to alphabet (not shift locked).
+//        releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED);
+//
+//        // Set capitalize the first character of all words mode.
+//        setAutoCapsMode(CAP_MODE_WORDS);
+//        // Load keyboard, should be in automatic shifted.
+//        loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED);
+//        // First shift key tap.
+//        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+//        // Second shift key tap, maybe shift locked.
+//        secondPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
+//        // Press/release letter key, remain in manual shifted.
+//        chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+//        // Release shift key, back to alphabet (not shift locked).
+//        releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED);
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
new file mode 100644
index 0000000..99fbc96
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
@@ -0,0 +1,316 @@
+/*
+ * 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.keyboard.internal;
+
+import android.test.AndroidTestCase;
+
+public class PointerTrackerQueueTests extends AndroidTestCase {
+    public static class Element implements PointerTrackerQueue.ElementActions {
+        public static int sPhantomUpCount;
+        public static final long NOT_HAPPENED = -1;
+
+        public final int mId;
+        public boolean mIsModifier;
+        public boolean mIsInSlidingKeyInput;
+        public long mPhantomUpEventTime = NOT_HAPPENED;
+
+        public Element(int id) {
+            mId = id;
+        }
+
+        @Override
+        public boolean isModifier() {
+            return mIsModifier;
+        }
+
+        @Override
+        public boolean isInSlidingKeyInput() {
+            return mIsInSlidingKeyInput;
+        }
+
+        @Override
+        public void onPhantomUpEvent(long eventTime) {
+            sPhantomUpCount++;
+            mPhantomUpEventTime = eventTime + sPhantomUpCount;
+        }
+
+        @Override
+        public String toString() {
+            return Integer.toString(mId);
+        }
+    }
+
+    private final Element mElement1 = new Element(1);
+    private final Element mElement2 = new Element(2);
+    private final Element mElement3 = new Element(3);
+    private final Element mElement4 = new Element(4);
+    private final PointerTrackerQueue mQueue = new PointerTrackerQueue();
+
+    public void testEmpty() {
+        assertEquals("empty queue", 0, mQueue.size());
+        assertEquals("empty queue", "[]", mQueue.toString());
+    }
+
+    public void testAdd() {
+        mQueue.add(mElement1);
+        assertEquals("add element1", 1, mQueue.size());
+        assertEquals("after adding element1", "[1]", mQueue.toString());
+        mQueue.add(mElement2);
+        assertEquals("add element2", 2, mQueue.size());
+        assertEquals("after adding element2", "[1 2]", mQueue.toString());
+        mQueue.add(mElement3);
+        assertEquals("add element3", 3, mQueue.size());
+        assertEquals("after adding element3", "[1 2 3]", mQueue.toString());
+        mQueue.add(mElement4);
+        assertEquals("add element4", 4, mQueue.size());
+        assertEquals("after adding element4", "[1 2 3 4]", mQueue.toString());
+    }
+
+    public void testRemove() {
+        Element.sPhantomUpCount = 0;
+
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        mQueue.remove(mElement2);
+        assertEquals("remove element2", 3, mQueue.size());
+        assertEquals("after removing element2", "[1 3 4]", mQueue.toString());
+        mQueue.remove(mElement4);
+        assertEquals("remove element4", 2, mQueue.size());
+        assertEquals("after removing element4", "[1 3]", mQueue.toString());
+        mQueue.remove(mElement4);
+        assertEquals("remove element4 again", 2, mQueue.size());
+        assertEquals("after removing element4 again", "[1 3]", mQueue.toString());
+        mQueue.remove(mElement1);
+        assertEquals("remove element1", 1, mQueue.size());
+        assertEquals("after removing element4", "[3]", mQueue.toString());
+        mQueue.remove(mElement3);
+        assertEquals("remove element3", 0, mQueue.size());
+        assertEquals("after removing element3", "[]", mQueue.toString());
+        mQueue.remove(mElement1);
+        assertEquals("remove element1 again", 0, mQueue.size());
+        assertEquals("after removing element1 again", "[]", mQueue.toString());
+
+        assertEquals("after remove elements", 0, Element.sPhantomUpCount);
+        assertEquals("after remove element1",
+                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals("after remove element2",
+                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals("after remove element3",
+                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals("after remove element4",
+                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testAddAndRemove() {
+        Element.sPhantomUpCount = 0;
+
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        mQueue.remove(mElement2);
+        assertEquals("remove element2", 3, mQueue.size());
+        assertEquals("after removing element2", "[1 3 4]", mQueue.toString());
+        mQueue.remove(mElement4);
+        assertEquals("remove element4", 2, mQueue.size());
+        assertEquals("after removing element4", "[1 3]", mQueue.toString());
+        mQueue.add(mElement2);
+        assertEquals("add element2", 3, mQueue.size());
+        assertEquals("after adding element2", "[1 3 2]", mQueue.toString());
+        mQueue.remove(mElement4);
+        assertEquals("remove element4 again", 3, mQueue.size());
+        assertEquals("after removing element4 again", "[1 3 2]", mQueue.toString());
+        mQueue.remove(mElement1);
+        assertEquals("remove element1", 2, mQueue.size());
+        assertEquals("after removing element4", "[3 2]", mQueue.toString());
+        mQueue.add(mElement1);
+        assertEquals("add element1", 3, mQueue.size());
+        assertEquals("after adding element1", "[3 2 1]", mQueue.toString());
+        mQueue.remove(mElement3);
+        assertEquals("remove element3", 2, mQueue.size());
+        assertEquals("after removing element3", "[2 1]", mQueue.toString());
+        mQueue.remove(mElement1);
+        assertEquals("remove element1 again", 1, mQueue.size());
+        assertEquals("after removing element1 again", "[2]", mQueue.toString());
+
+        assertEquals("after remove element1",
+                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals("after remove element2",
+                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals("after remove element3",
+                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals("after remove element4",
+                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testReleaseAllPointers() {
+        mElement2.mIsModifier = true;
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        final long eventTime = 123;
+        Element.sPhantomUpCount = 0;
+        mQueue.releaseAllPointers(eventTime);
+        assertEquals("after releaseAllPointers", 4, Element.sPhantomUpCount);
+        assertEquals("after releaseAllPointers", 0, mQueue.size());
+        assertEquals("after releaseAllPointers", "[]", mQueue.toString());
+        assertEquals("after releaseAllPointers element1",
+                eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointers element2",
+                eventTime + 2, mElement2.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointers element3",
+                eventTime + 3, mElement3.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointers element4",
+                eventTime + 4, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testReleaseAllPointersOlderThan() {
+        mElement2.mIsModifier = true;
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        final long eventTime = 123;
+        Element.sPhantomUpCount = 0;
+        mQueue.releaseAllPointersOlderThan(mElement4, eventTime);
+        assertEquals("after releaseAllPointersOlderThan", 2, Element.sPhantomUpCount);
+        assertEquals("after releaseAllPointersOlderThan", 2, mQueue.size());
+        assertEquals("after releaseAllPointersOlderThan", "[2 4]", mQueue.toString());
+        assertEquals("after releaseAllPointersOlderThan element1",
+                eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersOlderThan element2",
+                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersOlderThan element3",
+                eventTime + 2, mElement3.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersOlderThan element4",
+                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testReleaseAllPointersOlderThanWithoutModifier() {
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        final long eventTime = 123;
+        Element.sPhantomUpCount = 0;
+        mQueue.releaseAllPointersOlderThan(mElement4, eventTime);
+        assertEquals("after releaseAllPointersOlderThan without modifier",
+                3, Element.sPhantomUpCount);
+        assertEquals("after releaseAllPointersOlderThan without modifier", 1, mQueue.size());
+        assertEquals("after releaseAllPointersOlderThan without modifier",
+                "[4]", mQueue.toString());
+        assertEquals("after releaseAllPointersOlderThan without modifier element1",
+                eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersOlderThan without modifier element2",
+                eventTime + 2, mElement2.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersOlderThan without modifier element3",
+                eventTime + 3, mElement3.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersOlderThan without modifier element4",
+                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testReleaseAllPointersExcept() {
+        mElement2.mIsModifier = true;
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        final long eventTime = 123;
+        Element.sPhantomUpCount = 0;
+        mQueue.releaseAllPointersExcept(mElement3, eventTime);
+        assertEquals("after releaseAllPointersExcept", 3, Element.sPhantomUpCount);
+        assertEquals("after releaseAllPointersExcept", 1, mQueue.size());
+        assertEquals("after releaseAllPointersExcept", "[3]", mQueue.toString());
+        assertEquals("after releaseAllPointersExcept element1",
+                eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersExcept element2",
+                eventTime + 2, mElement2.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersExcept element3",
+                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals("after releaseAllPointersExcept element4",
+                eventTime + 3, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testHasModifierKeyOlderThan() {
+        Element.sPhantomUpCount = 0;
+        assertFalse("hasModifierKeyOlderThan empty", mQueue.hasModifierKeyOlderThan(mElement1));
+
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        assertFalse("hasModifierKeyOlderThan element1", mQueue.hasModifierKeyOlderThan(mElement1));
+        assertFalse("hasModifierKeyOlderThan element2", mQueue.hasModifierKeyOlderThan(mElement2));
+        assertFalse("hasModifierKeyOlderThan element3", mQueue.hasModifierKeyOlderThan(mElement3));
+        assertFalse("hasModifierKeyOlderThan element4", mQueue.hasModifierKeyOlderThan(mElement4));
+
+        mElement2.mIsModifier = true;
+        assertFalse("hasModifierKeyOlderThan element1", mQueue.hasModifierKeyOlderThan(mElement1));
+        assertFalse("hasModifierKeyOlderThan element2", mQueue.hasModifierKeyOlderThan(mElement2));
+        assertTrue("hasModifierKeyOlderThan element3", mQueue.hasModifierKeyOlderThan(mElement3));
+        assertTrue("hasModifierKeyOlderThan element4", mQueue.hasModifierKeyOlderThan(mElement4));
+
+        assertEquals("after hasModifierKeyOlderThan", 0, Element.sPhantomUpCount);
+        assertEquals("after hasModifierKeyOlderThan", 4, mQueue.size());
+        assertEquals("after hasModifierKeyOlderThan", "[1 2 3 4]", mQueue.toString());
+        assertEquals("after hasModifierKeyOlderThan element1",
+                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals("after hasModifierKeyOlderThan element2",
+                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals("after hasModifierKeyOlderThan element3",
+                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals("after hasModifierKeyOlderThan element4",
+                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testIsAnyInSlidingKeyInput() {
+        Element.sPhantomUpCount = 0;
+        assertFalse("isAnyInSlidingKeyInput empty", mQueue.isAnyInSlidingKeyInput());
+
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        assertFalse("isAnyInSlidingKeyInput element1", mQueue.isAnyInSlidingKeyInput());
+
+        mElement3.mIsInSlidingKeyInput = true;
+        assertTrue("isAnyInSlidingKeyInput element1", mQueue.isAnyInSlidingKeyInput());
+
+        assertEquals("after isAnyInSlidingKeyInput", 0, Element.sPhantomUpCount);
+        assertEquals("after isAnyInSlidingKeyInput", 4, mQueue.size());
+        assertEquals("after isAnyInSlidingKeyInput", "[1 2 3 4]", mQueue.toString());
+        assertEquals("after isAnyInSlidingKeyInput element1",
+                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals("after isAnyInSlidingKeyInput element2",
+                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals("after isAnyInSlidingKeyInput element3",
+                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals("after isAnyInSlidingKeyInput element4",
+                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+    }
+}