Merge "Refactor more keys menu framework (part 1)"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 992282b..db16917 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -102,7 +102,8 @@
  * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintActivatedColor
  * @attr ref R.styleable#Keyboard_Key_keyPreviewTextColor
  */
-public class KeyboardView extends View implements PointerTracker.DrawingProxy {
+public class KeyboardView extends View implements PointerTracker.DrawingProxy,
+        MoreKeysPanel.Controller {
     private static final String TAG = KeyboardView.class.getSimpleName();
 
     // XML attributes
@@ -137,6 +138,10 @@
     private final PreviewPlacerView mPreviewPlacerView;
     private final int[] mOriginCoords = CoordinateUtils.newInstance();
 
+    // More keys panel (used by both more keys keyboard and more suggestions view)
+    // TODO: Consider extending to support multiple more keys panels
+    protected MoreKeysPanel mMoreKeysPanel;
+
     // Key preview
     private static final int PREVIEW_ALPHA = 240;
     private final int mKeyPreviewLayoutId;
@@ -1014,7 +1019,33 @@
     }
 
     @Override
+    public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
+        if (isShowingMoreKeysPanel()) {
+            onDismissMoreKeysPanel();
+        }
+        mMoreKeysPanel = panel;
+        mPreviewPlacerView.addView(mMoreKeysPanel.getContainerView());
+    }
+
+    public boolean isShowingMoreKeysPanel() {
+        return (mMoreKeysPanel != null);
+    }
+
+    @Override
     public boolean dismissMoreKeysPanel() {
+        if (isShowingMoreKeysPanel()) {
+            return mMoreKeysPanel.dismissMoreKeysPanel();
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onDismissMoreKeysPanel() {
+        if (isShowingMoreKeysPanel()) {
+            mPreviewPlacerView.removeView(mMoreKeysPanel.getContainerView());
+            mMoreKeysPanel = null;
+            return true;
+        }
         return false;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index b9ce4fb..7d7eedb 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -39,7 +39,6 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.PopupWindow;
 
 import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
@@ -136,8 +135,6 @@
     private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
 
     // More keys keyboard
-    private PopupWindow mMoreKeysWindow;
-    private MoreKeysPanel mMoreKeysPanel;
     private int mMoreKeysPanelPointerTrackerId;
     private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
             new WeakHashMap<Key, MoreKeysPanel>();
@@ -665,12 +662,6 @@
             }
             mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
         }
-        if (mMoreKeysWindow == null) {
-            mMoreKeysWindow = new PopupWindow(getContext());
-            mMoreKeysWindow.setBackgroundDrawable(null);
-            mMoreKeysWindow.setAnimationStyle(R.style.MoreKeysKeyboardAnimation);
-        }
-        mMoreKeysPanel = moreKeysPanel;
         mMoreKeysPanelPointerTrackerId = tracker.mPointerId;
 
         final int[] lastCoords = CoordinateUtils.newInstance();
@@ -688,12 +679,11 @@
         // {@code mPreviewVisibleOffset} has been set appropriately in
         // {@link KeyboardView#showKeyPreview(PointerTracker)}.
         final int pointY = parentKey.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
-        moreKeysPanel.showMoreKeysPanel(
-                this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener);
+        moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
         final int translatedX = moreKeysPanel.translateX(CoordinateUtils.x(lastCoords));
         final int translatedY = moreKeysPanel.translateY(CoordinateUtils.y(lastCoords));
         tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
-        dimEntireKeyboard(true);
+        dimEntireKeyboard(true /* dimmed */);
         return true;
     }
 
@@ -876,15 +866,10 @@
     }
 
     @Override
-    public boolean dismissMoreKeysPanel() {
-        if (mMoreKeysWindow == null || !mMoreKeysWindow.isShowing()) {
-            return false;
-        }
-        mMoreKeysWindow.dismiss();
-        mMoreKeysPanel = null;
+    public boolean onDismissMoreKeysPanel() {
         mMoreKeysPanelPointerTrackerId = -1;
-        dimEntireKeyboard(false);
-        return true;
+        dimEntireKeyboard(false /* dimmed */);
+        return super.onDismissMoreKeysPanel();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index a698b0b..16606a1 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -19,9 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.View;
-import android.widget.PopupWindow;
 
 import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
@@ -154,37 +152,33 @@
 
     @Override
     public void showMoreKeysPanel(final View parentView, final Controller controller,
-            final int pointX, final int pointY, final PopupWindow window,
-            final KeyboardActionListener listener) {
+            final int pointX, final int pointY, final KeyboardActionListener listener) {
         mController = controller;
         mListener = listener;
-        final View container = (View)getParent();
+        final View container = getContainerView();
         final MoreKeysKeyboard pane = (MoreKeysKeyboard)getKeyboard();
         final int defaultCoordX = pane.getDefaultCoordX();
         // The coordinates of panel's left-top corner in parentView's coordinate system.
         final int x = pointX - defaultCoordX - container.getPaddingLeft();
         final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom();
 
-        window.setContentView(container);
-        window.setWidth(container.getMeasuredWidth());
-        window.setHeight(container.getMeasuredHeight());
         parentView.getLocationInWindow(mCoordinates);
-        window.showAtLocation(parentView, Gravity.NO_GRAVITY,
-                x + CoordinateUtils.x(mCoordinates), y + CoordinateUtils.y(mCoordinates));
+        // Ensure the horizontal position of the panel does not extend past the screen edges.
+        final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
+        final int panelX = Math.max(0, Math.min(maxX, x + CoordinateUtils.x(mCoordinates)));
+        final int panelY = y + CoordinateUtils.y(mCoordinates);
+        container.setX(panelX);
+        container.setY(panelY);
 
         mOriginX = x + container.getPaddingLeft();
         mOriginY = y + container.getPaddingTop();
+        controller.onShowMoreKeysPanel(this);
     }
 
-    private boolean mIsDismissing;
-
     @Override
     public boolean dismissMoreKeysPanel() {
-        if (mIsDismissing || mController == null) return false;
-        mIsDismissing = true;
-        final boolean dismissed = mController.dismissMoreKeysPanel();
-        mIsDismissing = false;
-        return dismissed;
+        if (mController == null) return false;
+        return mController.onDismissMoreKeysPanel();
     }
 
     @Override
@@ -196,4 +190,14 @@
     public int translateY(final int y) {
         return y - mOriginY;
     }
+
+    @Override
+    public View getContainerView() {
+        return (View)getParent();
+    }
+
+    @Override
+    public boolean isShowingInParent() {
+        return (getContainerView().getParent() != null);
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
index f9a196d..8bcddcc 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
@@ -17,25 +17,42 @@
 package com.android.inputmethod.keyboard;
 
 import android.view.View;
-import android.widget.PopupWindow;
 
 public interface MoreKeysPanel extends PointerTracker.KeyEventHandler {
     public interface Controller {
-        public boolean dismissMoreKeysPanel();
+        /**
+         * Add the {@link MoreKeysPanel} to the target view.
+         * @param panel
+         */
+        public void onShowMoreKeysPanel(final MoreKeysPanel panel);
+
+        /**
+         * Remove the current {@link MoreKeysPanel} to the target view.
+         */
+        public boolean onDismissMoreKeysPanel();
     }
 
     /**
-     * Show more keys panel.
+     * Initializes the layout and event handling of this {@link MoreKeysPanel} and calls the
+     * controller's onShowMoreKeysPanel to add the panel's container view.
      *
-     * @param parentView the parent view of this more keys panel
-     * @param controller the controller that can dismiss this more keys panel
-     * @param pointX x coordinate of this more keys panel
-     * @param pointY y coordinate of this more keys panel
-     * @param window PopupWindow to be used to show this more keys panel
-     * @param listener the listener that will receive keyboard action from this more keys panel.
+     * @param parentView the parent view of this {@link MoreKeysPanel}
+     * @param controller the controller that can dismiss this {@link MoreKeysPanel}
+     * @param pointX x coordinate of this {@link MoreKeysPanel}
+     * @param pointY y coordinate of this {@link MoreKeysPanel}
+     * @param listener the listener that will receive keyboard action from this
+     * {@link MoreKeysPanel}.
      */
-    public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
-            PopupWindow window, KeyboardActionListener listener);
+    // TODO: Currently the MoreKeysPanel is inside a container view that is added to the parent.
+    // Consider the simpler approach of placing the MoreKeysPanel itself into the parent view.
+    public void showMoreKeysPanel(View parentView, Controller controller, int pointX,
+            int pointY, KeyboardActionListener listener);
+
+    /**
+     * Dismisses the more keys panel and calls the controller's onDismissMoreKeysPanel to remove
+     * the panel's container view.
+     */
+    public boolean dismissMoreKeysPanel();
 
     /**
      * Translate X-coordinate of touch event to the local X-coordinate of this
@@ -54,4 +71,14 @@
      * @return the local Y-coordinate to this {@link MoreKeysPanel}
      */
     public int translateY(int y);
+
+    /**
+     * Return the view containing the more keys panel.
+     */
+    public View getContainerView();
+
+    /**
+     * Return whether the panel is currently being shown.
+     */
+    public boolean isShowingInParent();
 }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 20a299e..a44f3ed 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -77,13 +77,14 @@
         public TimerProxy getTimerProxy();
     }
 
-    public interface DrawingProxy extends MoreKeysPanel.Controller {
+    public interface DrawingProxy {
         public void invalidateKey(Key key);
         public void showKeyPreview(PointerTracker tracker);
         public void dismissKeyPreview(PointerTracker tracker);
         public void showSlidingKeyInputPreview(PointerTracker tracker);
         public void dismissSlidingKeyInputPreview();
         public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker);
+        public boolean dismissMoreKeysPanel();
     }
 
     public interface TimerProxy {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 305f38d..dcbbfca 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1064,12 +1064,13 @@
         final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0
                 : mSuggestionsContainer.getHeight();
         final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
-        int touchY = extraHeight;
+        int visibleTopY = extraHeight;
         // Need to set touchable region only if input view is being shown
         if (mainKeyboardView.isShown()) {
             if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
-                touchY -= suggestionsHeight;
+                visibleTopY -= suggestionsHeight;
             }
+            final int touchY = mainKeyboardView.isShowingMoreKeysPanel() ? 0 : visibleTopY;
             final int touchWidth = mainKeyboardView.getWidth();
             final int touchHeight = mainKeyboardView.getHeight() + extraHeight
                     // Extend touchable region below the keyboard.
@@ -1077,8 +1078,8 @@
             outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
             outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight);
         }
-        outInsets.contentTopInsets = touchY;
-        outInsets.visibleTopInsets = touchY;
+        outInsets.contentTopInsets = visibleTopY;
+        outInsets.visibleTopInsets = visibleTopY;
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 8cc09f3..f0017c0 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -19,10 +19,8 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.widget.PopupWindow;
 
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.Keyboard;
@@ -148,37 +146,33 @@
 
     @Override
     public void showMoreKeysPanel(final View parentView, final Controller controller,
-            final int pointX, final int pointY, final PopupWindow window,
-            final KeyboardActionListener listener) {
+            final int pointX, final int pointY, final KeyboardActionListener listener) {
         mController = controller;
         mListener = listener;
-        final View container = (View)getParent();
+        final View container = getContainerView();
         final MoreSuggestions pane = (MoreSuggestions)getKeyboard();
         final int defaultCoordX = pane.mOccupiedWidth / 2;
         // The coordinates of panel's left-top corner in parentView's coordinate system.
         final int x = pointX - defaultCoordX - container.getPaddingLeft();
         final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom();
 
-        window.setContentView(container);
-        window.setWidth(container.getMeasuredWidth());
-        window.setHeight(container.getMeasuredHeight());
         parentView.getLocationInWindow(mCoordinates);
-        window.showAtLocation(parentView, Gravity.NO_GRAVITY,
-                x + CoordinateUtils.x(mCoordinates), y + CoordinateUtils.y(mCoordinates));
+        // Ensure the horizontal position of the panel does not extend past the screen edges.
+        final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
+        final int panelX = Math.max(0, Math.min(maxX, x + CoordinateUtils.x(mCoordinates)));
+        final int panelY = y + CoordinateUtils.y(mCoordinates);
+        container.setX(panelX);
+        container.setY(panelY);
 
         mOriginX = x + container.getPaddingLeft();
         mOriginY = y + container.getPaddingTop();
+        controller.onShowMoreKeysPanel(this);
     }
 
-    private boolean mIsDismissing;
-
     @Override
     public boolean dismissMoreKeysPanel() {
-        if (mIsDismissing || mController == null) return false;
-        mIsDismissing = true;
-        final boolean dismissed = mController.dismissMoreKeysPanel();
-        mIsDismissing = false;
-        return dismissed;
+        if (mController == null) return false;
+        return mController.onDismissMoreKeysPanel();
     }
 
     @Override
@@ -225,4 +219,14 @@
         tracker.processMotionEvent(action, x, y, eventTime, mModalPanelKeyEventHandler);
         return true;
     }
+
+    @Override
+    public View getContainerView() {
+        return (View)getParent();
+    }
+
+    @Override
+    public boolean isShowingInParent() {
+        return (getContainerView().getParent() != null);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e7cb97f..9fc2bf9 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -27,7 +27,6 @@
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Message;
 import android.text.Spannable;
@@ -91,7 +90,6 @@
     private final View mMoreSuggestionsContainer;
     private final MoreSuggestionsView mMoreSuggestionsView;
     private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
-    private final PopupWindow mMoreSuggestionsWindow;
 
     private final ArrayList<TextView> mWords = CollectionUtils.newArrayList();
     private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList();
@@ -641,21 +639,6 @@
                 .findViewById(R.id.more_suggestions_view);
         mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView);
 
-        final PopupWindow moreWindow = new PopupWindow(context);
-        moreWindow.setWindowLayoutMode(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        moreWindow.setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));
-        moreWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-        moreWindow.setFocusable(true);
-        moreWindow.setOutsideTouchable(true);
-        moreWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
-            @Override
-            public void onDismiss() {
-                mKeyboardView.dimEntireKeyboard(false);
-            }
-        });
-        mMoreSuggestionsWindow = moreWindow;
-
         final Resources res = context.getResources();
         mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset(
                 R.dimen.more_suggestions_modal_tolerance);
@@ -738,17 +721,19 @@
     private final MoreKeysPanel.Controller mMoreSuggestionsController =
             new MoreKeysPanel.Controller() {
         @Override
-        public boolean dismissMoreKeysPanel() {
-            return dismissMoreSuggestions();
+        public boolean onDismissMoreKeysPanel() {
+            mKeyboardView.dimEntireKeyboard(false /* dimmed */);
+            return mKeyboardView.onDismissMoreKeysPanel();
+        }
+
+        @Override
+        public void onShowMoreKeysPanel(MoreKeysPanel panel) {
+            mKeyboardView.onShowMoreKeysPanel(panel);
         }
     };
 
     boolean dismissMoreSuggestions() {
-        if (mMoreSuggestionsWindow.isShowing()) {
-            mMoreSuggestionsWindow.dismiss();
-            return true;
-        }
-        return false;
+        return mMoreSuggestionsView.dismissMoreKeysPanel();
     }
 
     @Override
@@ -780,11 +765,11 @@
         final int pointX = stripWidth / 2;
         final int pointY = -params.mMoreSuggestionsBottomGap;
         moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
-                mMoreSuggestionsWindow, mMoreSuggestionsListener);
+                mMoreSuggestionsListener);
         mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
         mOriginX = mLastX;
         mOriginY = mLastY;
-        mKeyboardView.dimEntireKeyboard(true);
+        mKeyboardView.dimEntireKeyboard(true /* dimmed */);
         for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
             mWords.get(i).setPressed(false);
         }
@@ -816,7 +801,7 @@
 
     @Override
     public boolean dispatchTouchEvent(final MotionEvent me) {
-        if (!mMoreSuggestionsWindow.isShowing()
+        if (!mMoreSuggestionsView.isShowingInParent()
                 || mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
             mLastX = (int)me.getX();
             mLastY = (int)me.getY();