Merge "Extend JNI interface to pass the theme id of the keybaord to the native."
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 13560e0..2e0cddc 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<LinearLayout
+<com.android.inputmethod.latin.InputView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     android:orientation="vertical"
@@ -61,4 +61,4 @@
         android:layout_alignParentBottom="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
-</LinearLayout>
+</com.android.inputmethod.latin.InputView>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 1776630..2a76321 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <declare-styleable name="KeyboardTheme">
+        <attr name="themeId" format="integer" />
         <!-- Keyboard style -->
         <attr name="keyboardStyle" format="reference" />
         <!-- LatinKeyboard style -->
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
index ce05980..9127235 100644
--- a/java/res/values/themes-basic-highcontrast.xml
+++ b/java/res/values/themes-basic-highcontrast.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.HighContrast" parent="KeyboardIcons">
+        <item name="themeId">1</item>
         <item name="keyboardStyle">@style/Keyboard</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
         <item name="keyboardViewStyle">@style/KeyboardView.HighContrast</item>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
index ff9fed5..6c0e16e 100644
--- a/java/res/values/themes-basic.xml
+++ b/java/res/values/themes-basic.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme" parent="KeyboardIcons">
+        <item name="themeId">0</item>
         <item name="keyboardStyle">@style/Keyboard</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
         <item name="keyboardViewStyle">@style/KeyboardView</item>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
index be853eb..43bff50 100644
--- a/java/res/values/themes-gingerbread.xml
+++ b/java/res/values/themes-gingerbread.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.Gingerbread" parent="KeyboardIcons">
+        <item name="themeId">8</item>
         <item name="keyboardStyle">@style/Keyboard.Gingerbread</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
         <item name="keyboardViewStyle">@style/KeyboardView.Gingerbread</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 618aaed..1235d4e 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.IceCreamSandwich" parent="KeyboardIcons.IceCreamSandwich">
+        <item name="themeId">5</item>
         <item name="keyboardStyle">@style/Keyboard.IceCreamSandwich</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard.IceCreamSandwich</item>
         <item name="keyboardViewStyle">@style/KeyboardView.IceCreamSandwich</item>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
index fdf9c51..6e25f41 100644
--- a/java/res/values/themes-stone-bold.xml
+++ b/java/res/values/themes-stone-bold.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.Stone.Bold" parent="KeyboardIcons.Black">
+        <item name="themeId">7</item>
         <item name="keyboardStyle">@style/Keyboard.Stone</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item>
         <item name="keyboardViewStyle">@style/KeyboardView.Stone.Bold</item>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
index cb3edc5..3cbda81 100644
--- a/java/res/values/themes-stone.xml
+++ b/java/res/values/themes-stone.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.Stone" parent="KeyboardIcons.Black">
+        <item name="themeId">6</item>
         <item name="keyboardStyle">@style/Keyboard.Stone</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item>
         <item name="keyboardViewStyle">@style/KeyboardView.Stone</item>
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 2f11164..d8f8bef 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -84,6 +84,7 @@
     public static final int CODE_UNSPECIFIED = -99;
 
     public final KeyboardId mId;
+    public final int mThemeId;
 
     /** Total height of the keyboard, including the padding and keys */
     public final int mOccupiedHeight;
@@ -121,6 +122,7 @@
 
     public Keyboard(KeyboardParams params) {
         mId = params.mId;
+        mThemeId = params.mThemeId;
         mOccupiedHeight = params.mOccupiedHeight;
         mOccupiedWidth = params.mOccupiedWidth;
         mMostCommonKeyHeight = params.mMostCommonKeyHeight;
@@ -238,4 +240,17 @@
     public int[] getNearestKeys(int x, int y) {
         return mProximityInfo.getNearestKeys(x, y);
     }
+
+    public static String themeName(int themeId) {
+        // This should be aligned with theme-*.xml resource files' themeId attribute.
+        switch (themeId) {
+        case 0: return "Basic";
+        case 1: return "BasicHighContrast";
+        case 5: return "IceCreamSandwich";
+        case 6: return "Stone";
+        case 7: return "StoneBold";
+        case 8: return "GingerBread";
+        default: return null;
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 2d12369..13e8ba1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -32,6 +32,7 @@
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
 import com.android.inputmethod.keyboard.internal.ModifierKeyState;
 import com.android.inputmethod.keyboard.internal.ShiftKeyState;
+import com.android.inputmethod.latin.InputView;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.LocaleUtils;
@@ -62,7 +63,7 @@
     private SubtypeSwitcher mSubtypeSwitcher;
     private SharedPreferences mPrefs;
 
-    private View mCurrentInputView;
+    private InputView mCurrentInputView;
     private LatinKeyboardView mKeyboardView;
     private LatinIME mInputMethodService;
     private String mPackageName;
@@ -230,6 +231,7 @@
     private void setKeyboard(final Keyboard keyboard) {
         final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
         mKeyboardView.setKeyboard(keyboard);
+        mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
         mCurrentId = keyboard.mId;
         mSwitchState = getSwitchState(mCurrentId);
         updateShiftLockState(keyboard);
@@ -273,10 +275,12 @@
 
             if (DEBUG_CACHE) {
                 Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
-                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
+                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id
+                        + " theme=" + Keyboard.themeName(keyboard.mThemeId));
             }
         } else if (DEBUG_CACHE) {
-            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
+            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id
+                    + " theme=" + Keyboard.themeName(keyboard.mThemeId));
         }
 
         keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
@@ -762,7 +766,7 @@
         for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
             try {
                 setContextThemeWrapper(mInputMethodService, newThemeIndex);
-                mCurrentInputView = LayoutInflater.from(mThemeContext).inflate(
+                mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
                         R.layout.input_view, null);
                 tryGC = false;
             } catch (OutOfMemoryError e) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 99b917c..48f683a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -244,6 +244,10 @@
 
         mParams = params;
 
+        final TypedArray a = context.obtainStyledAttributes(R.styleable.KeyboardTheme);
+        mParams.mThemeId = a.getInt(R.styleable.KeyboardTheme_themeId, 0);
+        a.recycle();
+
         mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
         mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index 01f9d3b..97f58fa 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -31,6 +31,7 @@
 
 public class KeyboardParams {
     public KeyboardId mId;
+    public int mThemeId;
 
     /** Total height and width of the keyboard, including the paddings and keys */
     public int mOccupiedHeight;
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
new file mode 100644
index 0000000..0dcb811
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 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.latin;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+public class InputView extends LinearLayout {
+    private View mSuggestionsContainer;
+    private View mKeyboardView;
+    private int mKeyboardTopPadding;
+
+    private boolean mIsForwardingEvent;
+    private final Rect mInputViewRect = new Rect();
+    private final Rect mEventForwardingRect = new Rect();
+    private final Rect mEventReceivingRect = new Rect();
+
+    public InputView(Context context, AttributeSet attrs) {
+        super(context, attrs, 0);
+    }
+
+    public void setKeyboardGeometry(int keyboardTopPadding) {
+        mKeyboardTopPadding = keyboardTopPadding;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mSuggestionsContainer = findViewById(R.id.suggestions_container);
+        mKeyboardView = findViewById(R.id.keyboard_view);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent me) {
+        if (mSuggestionsContainer.getVisibility() == VISIBLE
+                && mKeyboardView.getVisibility() == VISIBLE
+                && forwardTouchEvent(me)) {
+            return true;
+        }
+        return super.dispatchTouchEvent(me);
+    }
+
+    // The touch events that hit the top padding of keyboard should be forwarded to SuggestionsView.
+    private boolean forwardTouchEvent(MotionEvent me) {
+        final Rect rect = mInputViewRect;
+        this.getGlobalVisibleRect(rect);
+        final int x = (int)me.getX() + rect.left;
+        final int y = (int)me.getY() + rect.top;
+
+        final Rect forwardingRect = mEventForwardingRect;
+        mKeyboardView.getGlobalVisibleRect(forwardingRect);
+        if (!mIsForwardingEvent && !forwardingRect.contains(x, y)) {
+            return false;
+        }
+
+        final int forwardingLimitY = forwardingRect.top + mKeyboardTopPadding;
+        boolean sendToTarget = false;
+
+        switch (me.getAction()) {
+        case MotionEvent.ACTION_DOWN:
+            if (y < forwardingLimitY) {
+                // This down event and further move and up events should be forwarded to the target.
+                mIsForwardingEvent = true;
+                sendToTarget = true;
+            }
+            break;
+        case MotionEvent.ACTION_MOVE:
+            sendToTarget = mIsForwardingEvent;
+            break;
+        case MotionEvent.ACTION_UP:
+        case MotionEvent.ACTION_CANCEL:
+            sendToTarget = mIsForwardingEvent;
+            mIsForwardingEvent = false;
+            break;
+        }
+
+        if (!sendToTarget) {
+            return false;
+        }
+
+        final Rect receivingRect = mEventReceivingRect;
+        mSuggestionsContainer.getGlobalVisibleRect(receivingRect);
+        final int translatedX = x - receivingRect.left;
+        final int translatedY;
+        if (y < forwardingLimitY) {
+            // The forwarded event should have coordinates that are inside of the target.
+            translatedY = Math.min(y - receivingRect.top, receivingRect.height() - 1);
+        } else {
+            translatedY = y - receivingRect.top;
+        }
+        me.setLocation(translatedX, translatedY);
+        mSuggestionsContainer.dispatchTouchEvent(me);
+        return true;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ddda184..32649d5 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -241,7 +241,8 @@
         private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
         private static final int MSG_START_ORIENTATION_CHANGE = 8;
         private static final int MSG_START_INPUT_VIEW = 9;
-        private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 10;
+        private static final int MSG_DISPLAY_COMPLETIONS = 10;
+        private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 11;
 
         public UIHandler(LatinIME outerInstance) {
             super(outerInstance);
@@ -293,6 +294,9 @@
             case MSG_START_INPUT_VIEW:
                 latinIme.onStartInputView((EditorInfo)msg.obj, false);
                 break;
+            case MSG_DISPLAY_COMPLETIONS:
+                latinIme.onDisplayCompletions((CompletionInfo[])msg.obj);
+                break;
             case MSG_RESTORE_KEYBOARD_LAYOUT:
                 removeMessages(MSG_UPDATE_SHIFT_STATE);
                 ((KeyboardLayoutState)msg.obj).restore();
@@ -417,6 +421,18 @@
             }
             return false;
         }
+
+        public boolean postDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
+            if (hasMessages(MSG_START_INPUT_VIEW) || hasMessages(MSG_DISPLAY_COMPLETIONS)) {
+                removeMessages(MSG_DISPLAY_COMPLETIONS);
+                // Postpone onDisplayCompletions by ACCUMULATE_START_INPUT_VIEW_DELAY.
+                sendMessageDelayed(
+                        obtainMessage(MSG_DISPLAY_COMPLETIONS, applicationSpecifiedCompletions),
+                        ACCUMULATE_START_INPUT_VIEW_DELAY);
+                return true;
+            }
+            return false;
+        }
     }
 
     @Override
@@ -923,6 +939,9 @@
 
     @Override
     public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
+        if (mHandler.postDisplayCompletions(applicationSpecifiedCompletions)) {
+            return;
+        }
         if (DEBUG) {
             Log.i(TAG, "Received completions:");
             if (applicationSpecifiedCompletions != null) {
diff --git a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
index 031f35d..f6220ee 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
@@ -217,7 +217,7 @@
         int nodeIndex = findIndexOfChar(mRoot, word[charIndex]);
         while (CHARACTER_NOT_FOUND != nodeIndex) {
             currentGroup = currentNode.mData.get(nodeIndex);
-            differentCharIndex = compareArrays(currentGroup.mChars, word, charIndex) ;
+            differentCharIndex = compareArrays(currentGroup.mChars, word, charIndex);
             if (ARRAYS_ARE_EQUAL != differentCharIndex
                     && differentCharIndex < currentGroup.mChars.length) break;
             if (null == currentGroup.mChildren) break;
@@ -268,7 +268,7 @@
                                     + new String(word, 0, word.length));
                         }
                         final CharGroup newGroup = new CharGroup(word,
-                                currentGroup.mBigrams, frequency);
+                                currentGroup.mBigrams, frequency, currentGroup.mChildren);
                         currentNode.mData.set(nodeIndex, newGroup);
                     }
                 } else {