Fix NPE in LatinKeyboardBaseView

This change also refactors some mini-keyboard related methods in
LatinKeyboardBaseView and PointerTracker class.

Bug: 2973236
Change-Id: I3190fe89ea3ac3c6d351ed4b6d77de98a0aa65db
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
index 805bc2d..ba901ed 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
@@ -190,9 +190,7 @@
 
     // Popup mini keyboard
     private PopupWindow mMiniKeyboardPopup;
-    private View mMiniKeyboardContainer;
     private LatinKeyboardBaseView mMiniKeyboard;
-    private boolean mMiniKeyboardOnScreen;
     private View mMiniKeyboardParent;
     private Map<Key,View> mMiniKeyboardCache;
     private int mMiniKeyboardOriginX;
@@ -799,7 +797,7 @@
         }
         mInvalidatedKey = null;
         // Overlay a dark rectangle to dim the keyboard
-        if (mMiniKeyboardOnScreen) {
+        if (mMiniKeyboard != null) {
             paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
             canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
         }
@@ -967,14 +965,8 @@
             return false;
         boolean result = onLongPress(popupKey);
         if (result) {
+            tracker.setAlreadyProcessed();
             dismissKeyPreview();
-
-            long eventTime = tracker.getDownTime();
-            mMiniKeyboardPopupTime = eventTime;
-            MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN,
-                    tracker.getLastX(), tracker.getLastY(), eventTime);
-            mMiniKeyboard.onTouchEvent(downEvent);
-            downEvent.recycle();
         }
         return result;
     }
@@ -990,12 +982,12 @@
         int popupKeyboardId = popupKey.popupResId;
 
         if (popupKeyboardId != 0) {
-            mMiniKeyboardContainer = mMiniKeyboardCache.get(popupKey);
-            if (mMiniKeyboardContainer == null) {
+            View container = mMiniKeyboardCache.get(popupKey);
+            if (container == null) {
                 LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
                         Context.LAYOUT_INFLATER_SERVICE);
-                mMiniKeyboardContainer = inflater.inflate(mPopupLayout, null);
-                mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+                container = inflater.inflate(mPopupLayout, null);
+                mMiniKeyboard = (LatinKeyboardBaseView) container.findViewById(
                        R.id.LatinKeyboardBaseView);
                 mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() {
                     public void onKey(int primaryCode, int[] keyCodes, int x, int y) {
@@ -1029,13 +1021,13 @@
                 }
                 mMiniKeyboard.setKeyboard(keyboard);
                 mMiniKeyboard.setPopupParent(this);
-                mMiniKeyboardContainer.measure(
+                container.measure(
                         MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
                         MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
 
-                mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer);
+                mMiniKeyboardCache.put(popupKey, container);
             } else {
-                mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+                mMiniKeyboard = (LatinKeyboardBaseView) container.findViewById(
                         R.id.LatinKeyboardBaseView);
             }
             if (mWindowOffset == null) {
@@ -1044,35 +1036,36 @@
             }
             int popupX = popupKey.x + popupKey.width + getPaddingLeft();
             int popupY = popupKey.y + getPaddingTop();
-            popupX -= mMiniKeyboardContainer.getMeasuredWidth();
-            popupY -= mMiniKeyboardContainer.getMeasuredHeight();
+            popupX -= container.getMeasuredWidth();
+            popupY -= container.getMeasuredHeight();
             popupX += mWindowOffset[0];
             popupY += mWindowOffset[1];
-            final int x = popupX + mMiniKeyboardContainer.getPaddingRight();
-            final int y = popupY + mMiniKeyboardContainer.getPaddingBottom();
-            mMiniKeyboardOriginX = (x < 0 ? 0 : x) + mMiniKeyboardContainer.getPaddingLeft();
-            mMiniKeyboardOriginY = y + mMiniKeyboardContainer.getPaddingTop();
+            final int x = popupX + container.getPaddingRight();
+            final int y = popupY + container.getPaddingBottom();
+            mMiniKeyboardOriginX = (x < 0 ? 0 : x) + container.getPaddingLeft();
+            mMiniKeyboardOriginY = y + container.getPaddingTop();
             mMiniKeyboard.setPopupOffset((x < 0) ? 0 : x, y);
             mMiniKeyboard.setShifted(isShifted());
             mMiniKeyboard.setPreviewEnabled(isPreviewEnabled());
-            mMiniKeyboardPopup.setContentView(mMiniKeyboardContainer);
-            mMiniKeyboardPopup.setWidth(mMiniKeyboardContainer.getMeasuredWidth());
-            mMiniKeyboardPopup.setHeight(mMiniKeyboardContainer.getMeasuredHeight());
+            mMiniKeyboardPopup.setContentView(container);
+            mMiniKeyboardPopup.setWidth(container.getMeasuredWidth());
+            mMiniKeyboardPopup.setHeight(container.getMeasuredHeight());
             mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
-            mMiniKeyboardOnScreen = true;
 
-            // TODO: down event?
+            // Inject down event on the key to mini keyboard.
+            long eventTime = System.currentTimeMillis();
+            mMiniKeyboardPopupTime = eventTime;
+            MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN,
+                    popupKey.x + popupKey.width / 2, popupKey.y + popupKey.height / 2, eventTime);
+            mMiniKeyboard.onTouchEvent(downEvent);
+            downEvent.recycle();
+
             invalidateAllKeys();
             return true;
         }
         return false;
     }
 
-    // TODO: Should cleanup after refactoring mini-keyboard. 
-    public boolean isMiniKeyboardOnScreen() {
-        return mMiniKeyboardOnScreen;
-    }
-
     private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) {
         return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action,
                 x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0);
@@ -1107,7 +1100,7 @@
         mSwipeTracker.addMovement(me);
 
         // We must disable gesture detector while mini-keyboard is on the screen.
-        if (!mMiniKeyboardOnScreen && mGestureDetector.onTouchEvent(me)) {
+        if (mMiniKeyboard == null && mGestureDetector.onTouchEvent(me)) {
             dismissKeyPreview();
             mHandler.cancelKeyTimers();
             return true;
@@ -1115,7 +1108,7 @@
 
         // Needs to be called after the gesture detector gets a turn, as it may have
         // displayed the mini keyboard
-        if (mMiniKeyboardOnScreen) {
+        if (mMiniKeyboard != null) {
             MotionEvent translated = generateMiniKeyboardMotionEvent(action, (int)me.getX(),
                     (int)me.getY(), eventTime);
             mMiniKeyboard.onTouchEvent(translated);
@@ -1222,10 +1215,10 @@
         closing();
     }
 
-    public void dismissPopupKeyboard() {
+    private void dismissPopupKeyboard() {
         if (mMiniKeyboardPopup.isShowing()) {
             mMiniKeyboardPopup.dismiss();
-            mMiniKeyboardOnScreen = false;
+            mMiniKeyboard = null;
             mMiniKeyboardOriginX = 0;
             mMiniKeyboardOriginY = 0;
             invalidateAllKeys();
diff --git a/java/src/com/android/inputmethod/latin/PointerTracker.java b/java/src/com/android/inputmethod/latin/PointerTracker.java
index cb45e21..a4447d5 100644
--- a/java/src/com/android/inputmethod/latin/PointerTracker.java
+++ b/java/src/com/android/inputmethod/latin/PointerTracker.java
@@ -32,9 +32,6 @@
     public interface UIProxy {
         public void invalidateKey(Key key);
         public void showPreview(int keyIndex, PointerTracker tracker);
-        // TODO: These methods might be temporary.
-        public void dismissPopupKeyboard();
-        public boolean isMiniKeyboardOnScreen();
     }
 
     public final int mPointerId;
@@ -63,6 +60,9 @@
     private int mStartY;
     private long mDownTime;
 
+    // true if event is already translated to a key action (long press or mini-keyboard)
+    private boolean mKeyAlreadyProcessed;
+
     // for move de-bouncing
     private int mLastCodeX;
     private int mLastCodeY;
@@ -140,12 +140,17 @@
         }
     }
 
+    public void setAlreadyProcessed() {
+        mKeyAlreadyProcessed = true;
+    }
+
     public void onDownEvent(int x, int y, long eventTime) {
         int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
         mCurrentKey = keyIndex;
         mStartX = x;
         mStartY = y;
         mDownTime = eventTime;
+        mKeyAlreadyProcessed = false;
         startMoveDebouncing(x, y);
         startTimeDebouncing(eventTime);
         checkMultiTap(eventTime, keyIndex);
@@ -230,7 +235,7 @@
         }
         showKeyPreviewAndUpdateKey(NOT_A_KEY);
         // If we're not on a repeating key (which sends on a DOWN event)
-        if (!wasInKeyRepeat && !mProxy.isMiniKeyboardOnScreen()) {
+        if (!wasInKeyRepeat && !mKeyAlreadyProcessed) {
             detectAndSendKey(mCurrentKey, (int)x, (int)y, eventTime);
         }
         if (isValidKeyIndex(keyIndex))
@@ -242,7 +247,6 @@
             debugLog("onCancelEvt:", x, y);
         mHandler.cancelKeyTimers();
         mHandler.cancelPopupPreview();
-        mProxy.dismissPopupKeyboard();
         showKeyPreviewAndUpdateKey(NOT_A_KEY);
         int keyIndex = mCurrentKey;
         if (isValidKeyIndex(keyIndex))