Refactor PointerTracker class to include queue management

Change-Id: I81d025220df6d970d8758157a89168851c8421ae
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 7c6e4dc..dd552f0 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -1359,13 +1359,13 @@
             if (pointerCount == 1 && oldPointerCount == 2) {
                 // Multi-touch to single touch transition.
                 // Send a down event for the latest pointer.
-                tracker.onDownEvent(x, y, eventTime);
+                tracker.onDownEvent(x, y, eventTime, null);
             } else if (pointerCount == 2 && oldPointerCount == 1) {
                 // Single-touch to multi-touch transition.
                 // Send an up event for the last pointer.
-                tracker.onUpEvent(tracker.getLastX(), tracker.getLastY(), eventTime);
+                tracker.onUpEvent(tracker.getLastX(), tracker.getLastY(), eventTime, null);
             } else if (pointerCount == 1 && oldPointerCount == 1) {
-                tracker.onTouchEvent(action, x, y, eventTime);
+                tracker.onTouchEvent(action, x, y, eventTime, null);
             } else {
                 Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
                         + " (old " + oldPointerCount + ")");
@@ -1373,24 +1373,25 @@
             return true;
         }
 
+        final PointerTrackerQueue queue = mPointerQueue;
         if (action == MotionEvent.ACTION_MOVE) {
             for (int i = 0; i < pointerCount; i++) {
-                PointerTracker tracker = getPointerTracker(me.getPointerId(i));
-                tracker.onMoveEvent((int)me.getX(i), (int)me.getY(i), eventTime);
+                final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
+                tracker.onMoveEvent((int)me.getX(i), (int)me.getY(i), eventTime, queue);
             }
         } else {
-            PointerTracker tracker = getPointerTracker(id);
+            final PointerTracker tracker = getPointerTracker(id);
             switch (action) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_POINTER_DOWN:
-                onDownEvent(tracker, x, y, eventTime);
+                tracker.onDownEvent(x, y, eventTime, queue);
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_POINTER_UP:
-                onUpEvent(tracker, x, y, eventTime);
+                tracker.onUpEvent(x, y, eventTime, queue);
                 break;
             case MotionEvent.ACTION_CANCEL:
-                onCancelEvent(tracker, x, y, eventTime);
+                tracker.onCancelEvent(x, y, eventTime, queue);
                 break;
             }
         }
@@ -1398,39 +1399,6 @@
         return true;
     }
 
-    private void onDownEvent(PointerTracker tracker, int x, int y, long eventTime) {
-        if (tracker.isOnModifierKey(x, y)) {
-            // Before processing a down event of modifier key, all pointers already being tracked
-            // should be released.
-            mPointerQueue.releaseAllPointersExcept(null, eventTime);
-        }
-        tracker.onDownEvent(x, y, eventTime);
-        mPointerQueue.add(tracker);
-    }
-
-    private void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
-        if (tracker.isModifier()) {
-            // Before processing an up event of modifier key, all pointers already being tracked
-            // should be released.
-            mPointerQueue.releaseAllPointersExcept(tracker, eventTime);
-        } else {
-            int index = mPointerQueue.lastIndexOf(tracker);
-            if (index >= 0) {
-                mPointerQueue.releaseAllPointersOlderThan(tracker, eventTime);
-            } else {
-                Log.w(TAG, "onUpEvent: corresponding down event not found for pointer "
-                        + tracker.mPointerId);
-            }
-        }
-        tracker.onUpEvent(x, y, eventTime);
-        mPointerQueue.remove(tracker);
-    }
-
-    private void onCancelEvent(PointerTracker tracker, int x, int y, long eventTime) {
-        tracker.onCancelEvent(x, y, eventTime);
-        mPointerQueue.remove(tracker);
-    }
-
     protected void onSwipeDown() {
         mKeyboardActionListener.onSwipeDown();
     }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 9bfe911..552ab52 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -27,6 +27,7 @@
 
 public class PointerTracker {
     private static final String TAG = PointerTracker.class.getSimpleName();
+    private static final boolean ENABLE_ASSERTION = false;
     private static final boolean DEBUG_EVENT = false;
     private static final boolean DEBUG_MOVE_EVENT = false;
     private static final boolean DEBUG_LISTENER = false;
@@ -46,7 +47,6 @@
 
     // Miscellaneous constants
     private static final int NOT_A_KEY = KeyDetector.NOT_A_KEY;
-    private static final int[] KEY_DELETE = { Keyboard.CODE_DELETE };
 
     private final UIProxy mProxy;
     private final UIHandler mHandler;
@@ -181,7 +181,7 @@
         return isModifierInternal(mKeyState.getKeyIndex());
     }
 
-    public boolean isOnModifierKey(int x, int y) {
+    private boolean isOnModifierKey(int x, int y) {
         return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
     }
 
@@ -220,28 +220,51 @@
         mKeyAlreadyProcessed = true;
     }
 
-    public void onTouchEvent(int action, int x, int y, long eventTime) {
+    private void checkAssertion(PointerTrackerQueue queue) {
+        if (mHasDistinctMultitouch && queue == null)
+            throw new RuntimeException(
+                    "PointerTrackerQueue must be passed on distinct multi touch device");
+        if (!mHasDistinctMultitouch && queue != null)
+            throw new RuntimeException(
+                    "PointerTrackerQueue must be null on non-distinct multi touch device");
+    }
+
+    public void onTouchEvent(int action, int x, int y, long eventTime, PointerTrackerQueue queue) {
         switch (action) {
         case MotionEvent.ACTION_MOVE:
-            onMoveEvent(x, y, eventTime);
+            onMoveEvent(x, y, eventTime, queue);
             break;
         case MotionEvent.ACTION_DOWN:
         case MotionEvent.ACTION_POINTER_DOWN:
-            onDownEvent(x, y, eventTime);
+            onDownEvent(x, y, eventTime, queue);
             break;
         case MotionEvent.ACTION_UP:
         case MotionEvent.ACTION_POINTER_UP:
-            onUpEvent(x, y, eventTime);
+            onUpEvent(x, y, eventTime, queue);
             break;
         case MotionEvent.ACTION_CANCEL:
-            onCancelEvent(x, y, eventTime);
+            onCancelEvent(x, y, eventTime, queue);
             break;
         }
     }
 
-    public void onDownEvent(int x, int y, long eventTime) {
+    public void onDownEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
+        if (ENABLE_ASSERTION) checkAssertion(queue);
         if (DEBUG_EVENT)
             printTouchEvent("onDownEvent:", x, y, eventTime);
+
+        if (queue != null) {
+            if (isOnModifierKey(x, y)) {
+                // Before processing a down event of modifier key, all pointers already being
+                // tracked should be released.
+                queue.releaseAllPointers(eventTime);
+            }
+            queue.add(this);
+        }
+        onDownEventInternal(x, y, eventTime);
+    }
+
+    public void onDownEventInternal(int x, int y, long eventTime) {
         int keyIndex = mKeyState.onDownKey(x, y, eventTime);
         // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
         // from modifier key, or 3) this pointer is on mini-keyboard.
@@ -267,7 +290,8 @@
         showKeyPreviewAndUpdateKeyGraphics(keyIndex);
     }
 
-    public void onMoveEvent(int x, int y, long eventTime) {
+    public void onMoveEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
+        if (ENABLE_ASSERTION) checkAssertion(queue);
         if (DEBUG_MOVE_EVENT)
             printTouchEvent("onMoveEvent:", x, y, eventTime);
         if (mKeyAlreadyProcessed)
@@ -318,11 +342,31 @@
         showKeyPreviewAndUpdateKeyGraphics(mKeyState.getKeyIndex());
     }
 
-    public void onUpEvent(int pointX, int pointY, long eventTime) {
-        int x = pointX;
-        int y = pointY;
+    public void onUpEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
+        if (ENABLE_ASSERTION) checkAssertion(queue);
         if (DEBUG_EVENT)
             printTouchEvent("onUpEvent  :", x, y, eventTime);
+
+        if (queue != null) {
+            if (isModifier()) {
+                // Before processing an up event of modifier key, all pointers already being
+                // tracked should be released.
+                queue.releaseAllPointersExcept(this, eventTime);
+            } else {
+                queue.releaseAllPointersOlderThan(this, eventTime);
+            }
+            queue.remove(this);
+        }
+        onUpEventInternal(x, y, eventTime);
+    }
+
+    public void onUpEventForRelease(int x, int y, long eventTime) {
+        onUpEventInternal(x, y, eventTime);
+    }
+
+    private void onUpEventInternal(int pointX, int pointY, long eventTime) {
+        int x = pointX;
+        int y = pointY;
         mHandler.cancelKeyTimers();
         mHandler.cancelPopupPreview();
         showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
@@ -345,9 +389,17 @@
             mProxy.invalidateKey(mKeys[keyIndex]);
     }
 
-    public void onCancelEvent(int x, int y, long eventTime) {
+    public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
+        if (ENABLE_ASSERTION) checkAssertion(queue);
         if (DEBUG_EVENT)
             printTouchEvent("onCancelEvt:", x, y, eventTime);
+
+        if (queue != null)
+            queue.remove(this);
+        onCancelEventInternal(x, y, eventTime);
+    }
+
+    private void onCancelEventInternal(int x, int y, long eventTime) {
         mHandler.cancelKeyTimers();
         mHandler.cancelPopupPreview();
         showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
index 236c9d5..928f3cd 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
@@ -25,35 +25,32 @@
         mQueue.add(tracker);
     }
 
-    public int lastIndexOf(PointerTracker tracker) {
-        LinkedList<PointerTracker> queue = mQueue;
-        for (int index = queue.size() - 1; index >= 0; index--) {
-            PointerTracker t = queue.get(index);
-            if (t == tracker)
-                return index;
-        }
-        return -1;
-    }
-
     public void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) {
+        if (mQueue.lastIndexOf(tracker) < 0) {
+            return;
+        }
         LinkedList<PointerTracker> queue = mQueue;
         int oldestPos = 0;
         for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) {
             if (t.isModifier()) {
                 oldestPos++;
             } else {
-                t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
+                t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime);
                 t.setAlreadyProcessed();
                 queue.remove(oldestPos);
             }
         }
     }
 
+    public void releaseAllPointers(long eventTime) {
+        releaseAllPointersExcept(null, eventTime);
+    }
+
     public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
         for (PointerTracker t : mQueue) {
             if (t == tracker)
                 continue;
-            t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
+            t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime);
             t.setAlreadyProcessed();
         }
         mQueue.clear();