diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 5fecb30009087055091dac39d4c4bdcb37f6f69b..509b5b604fcb53f6a110220b73b158c709b8816d 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -73,6 +73,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
     private static final boolean DEBUG_SHOW_ALIGN = false;
     private static final boolean DEBUG_KEYBOARD_GRID = false;
 
+    private static final boolean ENABLE_CAPSLOCK_BY_LONGPRESS = false;
+    private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
+
     public static final int COLOR_SCHEME_WHITE = 0;
     public static final int COLOR_SCHEME_BLACK = 1;
 
@@ -265,8 +268,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
 
         public void startLongPressShiftTimer(long delay, int keyIndex, PointerTracker tracker) {
             cancelLongPressTimers();
-            sendMessageDelayed(
-                    obtainMessage(MSG_LONGPRESS_SHIFT_KEY, keyIndex, 0, tracker), delay);
+            if (ENABLE_CAPSLOCK_BY_LONGPRESS) {
+                sendMessageDelayed(
+                        obtainMessage(MSG_LONGPRESS_SHIFT_KEY, keyIndex, 0, tracker), delay);
+            }
         }
 
         public void cancelLongPressTimers() {
@@ -397,6 +402,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
 
         GestureDetector.SimpleOnGestureListener listener =
                 new GestureDetector.SimpleOnGestureListener() {
+            private boolean mProcessingDoubleTapEvent = false;
+
             @Override
             public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX,
                     float velocityY) {
@@ -432,6 +439,28 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
                 }
                 return false;
             }
+
+            @Override
+            public boolean onDoubleTap(MotionEvent e) {
+                if (ENABLE_CAPSLOCK_BY_DOUBLETAP && mKeyboard instanceof LatinKeyboard
+                        && ((LatinKeyboard) mKeyboard).isAlphaKeyboard()) {
+                    final int pointerIndex = e.getActionIndex();
+                    final int id = e.getPointerId(pointerIndex);
+                    final PointerTracker tracker = getPointerTracker(id);
+                    if (tracker.isOnShiftKey((int)e.getX(), (int)e.getY())) {
+                        onDoubleTapShiftKey(tracker);
+                        mProcessingDoubleTapEvent = true;
+                        return true;
+                    }
+                }
+                mProcessingDoubleTapEvent = false;
+                return false;
+            }
+
+            @Override
+            public boolean onDoubleTapEvent(MotionEvent e) {
+                return mProcessingDoubleTapEvent;
+            }
         };
 
         final boolean ignoreMultitouch = true;
@@ -1058,6 +1087,13 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
         mKeyboardActionListener.onKey(Keyboard.CODE_CAPSLOCK, null, 0, 0);
     }
 
+    private void onDoubleTapShiftKey(PointerTracker tracker) {
+        // When shift key is double tapped, the first tap is correctly processed as usual tap. And
+        // the second tap is treated as this double tap event, so that we need not mark tracker
+        // calling setAlreadyProcessed() nor remove the tracker from mPointerQueueueue.
+        mKeyboardActionListener.onKey(Keyboard.CODE_CAPSLOCK, null, 0, 0);
+    }
+
     private View inflateMiniKeyboardContainer(Key popupKey) {
         int popupKeyboardId = popupKey.mPopupResId;
         LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 269ae15301f1a00ee1e2658fd378b9c41bfb6e2d..a1210f240de2dcf22dcdee645d1adb43b6e312e4 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -219,6 +219,11 @@ public class PointerTracker {
         return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
     }
 
+    public boolean isOnShiftKey(int x, int y) {
+        final Key key = getKey(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
+        return key != null && key.mCodes[0] == Keyboard.CODE_SHIFT;
+    }
+
     public boolean isSpaceKey(int keyIndex) {
         Key key = getKey(keyIndex);
         return key != null && key.mCodes[0] == Keyboard.CODE_SPACE;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
index a3c0808282082495b6af37fe012a5e8e41a89ba4..e559b4cdee92b1178049868be86a666770860809 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
@@ -64,4 +64,16 @@ public class PointerTrackerQueue {
     public void remove(PointerTracker tracker) {
         mQueue.remove(tracker);
     }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("[");
+        for (PointerTracker tracker : mQueue) {
+            if (sb.length() > 1)
+                sb.append(" ");
+            sb.append(String.format("%d", tracker.mPointerId));
+        }
+        sb.append("]");
+        return sb.toString();
+    }
 }