diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 437bbf06ba7c0b6579ef62262027b2825a1474ed..4f6af98cade28741c8f2b4045dcd0372ab275280 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -23,7 +23,6 @@ import android.view.View;
 import android.widget.TextView;
 
 import com.android.inputmethod.keyboard.internal.GestureStroke;
-import com.android.inputmethod.keyboard.internal.GestureTracker;
 import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
 import com.android.inputmethod.latin.InputPointers;
 import com.android.inputmethod.latin.LatinImeLogger;
@@ -39,6 +38,10 @@ public class PointerTracker {
     private static final boolean DEBUG_LISTENER = false;
     private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
 
+    // TODO: There should be an option to turn on/off the gesture input.
+    private static final boolean GESTURE_ON = true;
+    private static final int MIN_RECOGNITION_TIME = 100; // msec
+
     public interface KeyEventHandler {
         /**
          * Get KeyDetector object that is used for this PointerTracker.
@@ -128,6 +131,13 @@ public class PointerTracker {
     private int mKeyQuarterWidthSquared;
     private final TextView mKeyPreviewText;
 
+    private boolean mIsAlphabetKeyboard;
+    private boolean mIsPossibleGesture = false;
+    private boolean mInGesture = false;
+
+    private int mLastRecognitionPointSize = 0;
+    private long mLastRecognitionTime = 0;
+
     // The position and time at which first down event occurred.
     private long mDownTime;
     private long mUpTime;
@@ -164,9 +174,6 @@ public class PointerTracker {
     private static final KeyboardActionListener EMPTY_LISTENER =
             new KeyboardActionListener.Adapter();
 
-    // Gesture tracker singleton instance
-    private static final GestureTracker sGestureTracker = GestureTracker.getInstance();
-
     private final GestureStroke mGestureStroke;
 
     public static void init(boolean hasDistinctMultitouch,
@@ -207,7 +214,6 @@ public class PointerTracker {
         for (final PointerTracker tracker : sTrackers) {
             tracker.mListener = listener;
         }
-        GestureTracker.init(listener);
     }
 
     public static void setKeyDetector(KeyDetector keyDetector) {
@@ -216,7 +222,6 @@ public class PointerTracker {
             // Mark that keyboard layout has been changed.
             tracker.mKeyboardLayoutHasBeenChanged = true;
         }
-        sGestureTracker.setKeyboard(keyDetector.getKeyboard());
     }
 
     public static void dismissAllKeyPreviews() {
@@ -226,35 +231,35 @@ public class PointerTracker {
         }
     }
 
-    // The working and returning object of the following methods,
-    // {@link #getIncrementalBatchPoints()} and {@link #getAllBatchPoints()}.
-    private static final InputPointers mAggregatedPointers = new InputPointers();
-
-    // TODO: This method is called only from GestureTracker and should address the thread-safty
-    // issue soon.
-    public static InputPointers getIncrementalBatchPoints() {
-        final InputPointers pointers = mAggregatedPointers;
-        pointers.reset();
+    // TODO: To handle multi-touch gestures we may want to move this method to
+    // {@link PointerTrackerQueue}.
+    private static InputPointers getIncrementalBatchPoints() {
+        final InputPointers pointers = new InputPointers();
+        // TODO: Add a default capacity parameter for the InputPointers' constructor.
+        // TODO: Avoid creating a new instance here?
         for (final PointerTracker tracker : sTrackers) {
-            tracker.getGestureStroke().appendIncrementalBatchPoints(pointers);
+            tracker.mGestureStroke.appendIncrementalBatchPoints(pointers);
         }
         return pointers;
     }
 
-    // TODO: This method is called only from GestureTracker and should address the thread-safety
-    // issue soon.
-    public static InputPointers getAllBatchPoints() {
-        final InputPointers pointers = mAggregatedPointers;
-        pointers.reset();
+    // TODO: To handle multi-touch gestures we may want to move this method to
+    // {@link PointerTrackerQueue}.
+    private static InputPointers getAllBatchPoints() {
+        // TODO: Add a default capacity parameter for the InputPointers' constructor.
+        // TODO: Avoid creating a new instance here?
+        final InputPointers pointers = new InputPointers();
         for (final PointerTracker tracker : sTrackers) {
-            tracker.getGestureStroke().appendAllBatchPoints(pointers);
+            tracker.mGestureStroke.appendAllBatchPoints(pointers);
         }
         return pointers;
     }
 
+    // TODO: To handle multi-touch gestures we may want to move this method to
+    // {@link PointerTrackerQueue}.
     public static void clearBatchInputPoints() {
         for (final PointerTracker tracker : sTrackers) {
-            tracker.getGestureStroke().reset();
+            tracker.mGestureStroke.reset();
         }
     }
 
@@ -274,13 +279,9 @@ public class PointerTracker {
         return mKeyPreviewText;
     }
 
-    public GestureStroke getGestureStroke() {
-        return mGestureStroke;
-    }
-
     // Returns true if keyboard has been changed by this callback.
     private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
-        if (sGestureTracker.isInGesture()) {
+        if (mInGesture) {
             return false;
         }
         final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
@@ -336,7 +337,7 @@ public class PointerTracker {
     // Note that we need primaryCode argument because the keyboard may in shifted state and the
     // primaryCode is different from {@link Key#mCode}.
     private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
-        if (sGestureTracker.isInGesture()) {
+        if (mInGesture) {
             return;
         }
         final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
@@ -369,6 +370,7 @@ public class PointerTracker {
     private void setKeyDetectorInner(KeyDetector keyDetector) {
         mKeyDetector = keyDetector;
         mKeyboard = keyDetector.getKeyboard();
+        mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard();
         mGestureStroke.setGestureSampleLength(
                 mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
         final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
@@ -441,7 +443,7 @@ public class PointerTracker {
             return;
         }
 
-        if (!key.noKeyPreview() && !sGestureTracker.isInGesture()) {
+        if (!key.noKeyPreview() && !mInGesture) {
             mDrawingProxy.showKeyPreview(this);
         }
         updatePressKeyGraphics(key);
@@ -512,6 +514,45 @@ public class PointerTracker {
         return newKey;
     }
 
+    private void startBatchInput() {
+        if (DEBUG_LISTENER) {
+            Log.d(TAG, "onStartBatchInput");
+        }
+        mInGesture = true;
+        mListener.onStartBatchInput();
+    }
+
+    private void updateBatchInput(InputPointers batchPoints) {
+        if (DEBUG_LISTENER) {
+            Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
+        }
+        mListener.onUpdateBatchInput(batchPoints);
+    }
+
+    private void endBatchInput(InputPointers batchPoints) {
+        if (DEBUG_LISTENER) {
+            Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
+        }
+        mListener.onEndBatchInput(batchPoints);
+        mInGesture = false;
+        clearBatchInputPoints();
+    }
+
+    private void abortBatchInput() {
+        mIsPossibleGesture = false;
+        mInGesture = false;
+    }
+
+    private boolean updateBatchInputRecognitionState(long eventTime, int size) {
+        if (size > mLastRecognitionPointSize
+                && eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) {
+            mLastRecognitionPointSize = size;
+            mLastRecognitionTime = eventTime;
+            return true;
+        }
+        return false;
+    }
+
     public void processMotionEvent(int action, int x, int y, long eventTime,
             KeyEventHandler handler) {
         switch (action) {
@@ -570,7 +611,13 @@ public class PointerTracker {
         }
         onDownEventInternal(x, y, eventTime);
         if (queue != null && queue.size() == 1) {
-            sGestureTracker.onDownEvent(this, x, y, eventTime, key);
+            mIsPossibleGesture = false;
+            // A gesture should start only from the letter key.
+            if (GESTURE_ON && mIsAlphabetKeyboard && key != null
+                    && Keyboard.isLetterCode(key.mCode)) {
+                mIsPossibleGesture = true;
+                mGestureStroke.addPoint(x, y, 0, false);
+            }
         }
     }
 
@@ -606,6 +653,25 @@ public class PointerTracker {
         mIsInSlidingKeyInput = true;
     }
 
+    private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
+            boolean isHistorical, Key key) {
+        final int gestureTime = (int)(eventTime - tracker.getDownTime());
+        if (GESTURE_ON && mIsPossibleGesture) {
+            final GestureStroke stroke = mGestureStroke;
+            stroke.addPoint(x, y, gestureTime, isHistorical);
+            if (!mInGesture && stroke.isStartOfAGesture(gestureTime)) {
+                startBatchInput();
+            }
+        }
+
+        if (key != null && mInGesture) {
+            final InputPointers batchPoints = getIncrementalBatchPoints();
+            if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
+                updateBatchInput(batchPoints);
+            }
+        }
+    }
+
     public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
         if (DEBUG_MOVE_EVENT)
             printTouchEvent("onMoveEvent:", x, y, eventTime);
@@ -620,7 +686,7 @@ public class PointerTracker {
                 final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
                 final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
                 final long historicalTime = me.getHistoricalEventTime(h);
-                sGestureTracker.onMoveEvent(this, historicalX, historicalY, historicalTime,
+                onGestureMoveEvent(this, historicalX, historicalY, historicalTime,
                         true /* isHistorical */, null);
             }
         }
@@ -631,8 +697,8 @@ public class PointerTracker {
         Key key = onMoveKey(x, y);
 
         // Register move event on gesture tracker.
-        sGestureTracker.onMoveEvent(this, x, y, eventTime, false, key);
-        if (sGestureTracker.isInGesture()) {
+        onGestureMoveEvent(this, x, y, eventTime, false /* isHistorical */, key);
+        if (mInGesture) {
             mIgnoreModifierKey = true;
             mTimerProxy.cancelLongPressTimer();
             mIsInSlidingKeyInput = true;
@@ -729,7 +795,7 @@ public class PointerTracker {
 
         final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
-            if (!sGestureTracker.isInGesture()) {
+            if (!mInGesture) {
                 if (mCurrentKey != null && mCurrentKey.isModifier()) {
                     // Before processing an up event of modifier key, all pointers already being
                     // tracked should be released.
@@ -763,21 +829,16 @@ public class PointerTracker {
             mIsShowingMoreKeysPanel = false;
         }
 
-        if (sGestureTracker.isInGesture()) {
+        if (mInGesture) {
             // Register up event on gesture tracker.
-            sGestureTracker.onUpEvent(this, x, y, eventTime);
-            if (!sPointerTrackerQueue.isAnyInSlidingKeyInput()) {
-                // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
-                sGestureTracker.endBatchInput();
-            }
+            // TODO: Figure out how to deal with multiple fingers that are in gesture, sliding,
+            // and/or tapping mode?
+            endBatchInput(getAllBatchPoints());
             if (mCurrentKey != null) {
                 callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
+                mCurrentKey = null;
             }
-            mCurrentKey = null;
             return;
-        } else {
-            // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
-            sGestureTracker.endBatchInput();
         }
 
         if (mKeyAlreadyProcessed)
@@ -791,8 +852,7 @@ public class PointerTracker {
         onLongPressed();
         onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
         mIsShowingMoreKeysPanel = true;
-        // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
-        sGestureTracker.abortBatchInput();
+        abortBatchInput();
     }
 
     public void onLongPressed() {
@@ -827,7 +887,7 @@ public class PointerTracker {
     }
 
     private void startRepeatKey(Key key) {
-        if (key != null && key.isRepeatable() && !sGestureTracker.isInGesture()) {
+        if (key != null && key.isRepeatable() && !mInGesture) {
             onRegisterKey(key);
             mTimerProxy.startKeyRepeatTimer(this);
         }
@@ -857,7 +917,7 @@ public class PointerTracker {
     }
 
     private void startLongPressTimer(Key key) {
-        if (key != null && key.isLongPressEnabled() && !sGestureTracker.isInGesture()) {
+        if (key != null && key.isLongPressEnabled() && !mInGesture) {
             mTimerProxy.startLongPressTimer(this);
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java
deleted file mode 100644
index fbfa663324610687acc432bcdeb20d022bfaa8c3..0000000000000000000000000000000000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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.util.Log;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.InputPointers;
-
-// TODO: Remove this class by consolidating with PointerTracker
-public class GestureTracker {
-    private static final String TAG = GestureTracker.class.getSimpleName();
-    private static final boolean DEBUG_LISTENER = false;
-
-    // TODO: There should be an option to turn on/off the gesture input.
-    private static final boolean GESTURE_ON = true;
-
-    private static final GestureTracker sInstance = new GestureTracker();
-
-    private static final int MIN_RECOGNITION_TIME = 100;
-
-    private boolean mIsAlphabetKeyboard;
-    private boolean mIsPossibleGesture = false;
-    private boolean mInGesture = false;
-
-    private KeyboardActionListener mListener;
-
-    private int mLastRecognitionPointSize = 0;
-    private long mLastRecognitionTime = 0;
-
-    public static void init(KeyboardActionListener listner) {
-        sInstance.mListener = listner;
-    }
-
-    public static GestureTracker getInstance() {
-        return sInstance;
-    }
-
-    private GestureTracker() {
-    }
-
-    public void setKeyboard(Keyboard keyboard) {
-        mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard();
-    }
-
-    private void startBatchInput() {
-        if (DEBUG_LISTENER) {
-            Log.d(TAG, "onStartBatchInput");
-        }
-        mInGesture = true;
-        mListener.onStartBatchInput();
-    }
-
-    // TODO: The corresponding startBatchInput() is a private method. Reorganize the code.
-    public void endBatchInput() {
-        if (isInGesture()) {
-            final InputPointers batchPoints = PointerTracker.getAllBatchPoints();
-            if (DEBUG_LISTENER) {
-                Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
-            }
-            mListener.onEndBatchInput(batchPoints);
-        }
-        mInGesture = false;
-        clearBatchInputPoints();
-    }
-
-    public void abortBatchInput() {
-        mIsPossibleGesture = false;
-        mInGesture = false;
-    }
-
-    public boolean isInGesture() {
-        return mInGesture;
-    }
-
-    public void onDownEvent(PointerTracker tracker, int x, int y, long eventTime, Key key) {
-        mIsPossibleGesture = false;
-        // A gesture should start only from the letter key.
-        if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) {
-            mIsPossibleGesture = true;
-            tracker.getGestureStroke().addPoint(x, y, 0, false);
-        }
-    }
-
-    public void onMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
-            boolean isHistorical, Key key) {
-        final int gestureTime = (int)(eventTime - tracker.getDownTime());
-        if (GESTURE_ON && mIsPossibleGesture) {
-            final GestureStroke stroke = tracker.getGestureStroke();
-            stroke.addPoint(x, y, gestureTime, isHistorical);
-            if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) {
-                startBatchInput();
-            }
-        }
-
-        if (key != null && isInGesture()) {
-            final InputPointers batchPoints = PointerTracker.getIncrementalBatchPoints();
-            if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
-                if (DEBUG_LISTENER) {
-                    Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
-                }
-                mListener.onUpdateBatchInput(batchPoints);
-            }
-        }
-    }
-
-    public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
-        if (isInGesture()) {
-            final InputPointers batchPoints = PointerTracker.getAllBatchPoints();
-            if (DEBUG_LISTENER) {
-                Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
-            }
-            mListener.onUpdateBatchInput(batchPoints);
-        }
-    }
-
-    private void clearBatchInputPoints() {
-        PointerTracker.clearBatchInputPoints();
-        mLastRecognitionPointSize = 0;
-        mLastRecognitionTime = 0;
-    }
-
-    private boolean updateBatchInputRecognitionState(long eventTime, int size) {
-        if (size > mLastRecognitionPointSize
-                && eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) {
-            mLastRecognitionPointSize = size;
-            mLastRecognitionTime = eventTime;
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index 5ad53480fc4264472c7f985382e91edcfdad531e..298e2b213cf9616edd5fec70d42c9e0741cf911e 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -60,6 +60,9 @@ public class InputPointers {
      * @param length the number of pointers to be appended.
      */
     public void append(InputPointers src, int startPos, int length) {
+        if (length == 0) {
+            return;
+        }
         mXCoordinates.append(src.mXCoordinates, startPos, length);
         mYCoordinates.append(src.mYCoordinates, startPos, length);
         mPointerIds.append(src.mPointerIds, startPos, length);