diff --git a/java/res/values/config-common.xml b/java/res/values/config-common.xml
index ad27ab4278b533b435e792ecf5b809a82783c2de..58f3e9827c04f4dd0505079746fa540cad93df61 100644
--- a/java/res/values/config-common.xml
+++ b/java/res/values/config-common.xml
@@ -48,6 +48,7 @@
     <integer name="config_max_longpress_timeout">700</integer>
     <integer name="config_min_longpress_timeout">100</integer>
     <integer name="config_longpress_timeout_step">10</integer>
+    <integer name="config_accessibility_long_press_key_timeout">1500</integer>
     <integer name="config_max_more_keys_column">5</integer>
     <integer name="config_more_keys_keyboard_fadein_anim_time">0</integer>
     <integer name="config_more_keys_keyboard_fadeout_anim_time">100</integer>
diff --git a/java/res/values/strings-talkback-descriptions.xml b/java/res/values/strings-talkback-descriptions.xml
index fa06362d8f5d8e7c449dd50bc58432d9a0abe3b4..09cc088a05cba57f0df25d24334e35842d418537 100644
--- a/java/res/values/strings-talkback-descriptions.xml
+++ b/java/res/values/strings-talkback-descriptions.xml
@@ -139,4 +139,9 @@
     <string name="spoken_symbol_unknown">Unknown symbol</string>
     <!-- Spoken description for unknown emoji code point. -->
     <string name="spoken_emoji_unknown">Unknown emoji</string>
+
+    <!-- Spoken descriptions when opening a more keys keyboard that has alternative characters. -->
+    <string name="spoken_open_more_keys_keyboard">Alternative characters are available</string>
+    <!-- Spoken descriptions when closing a more keys keyboard that has alternative characters. -->
+    <string name="spoken_close_more_keys_keyboard">Alternative characters are dismissed</string>
 </resources>
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java b/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java
new file mode 100644
index 0000000000000000000000000000000000000000..967cafad0c39f847b975cbded3ef1cdbc73480aa
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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.accessibility;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.R;
+
+// Handling long press timer to show a more keys keyboard.
+final class AccessibilityLongPressTimer extends Handler {
+    public interface LongPressTimerCallback {
+        public void onLongPressed(Key key);
+    }
+
+    private static final int MSG_LONG_PRESS = 1;
+
+    private final LongPressTimerCallback mCallback;
+    private final long mConfigAccessibilityLongPressTimeout;
+
+    public AccessibilityLongPressTimer(final LongPressTimerCallback callback,
+            final Context context) {
+        super();
+        mCallback = callback;
+        mConfigAccessibilityLongPressTimeout = context.getResources().getInteger(
+                R.integer.config_accessibility_long_press_key_timeout);
+    }
+
+    @Override
+    public void handleMessage(final Message msg) {
+        switch (msg.what) {
+        case MSG_LONG_PRESS:
+            cancelLongPress();
+            mCallback.onLongPressed((Key)msg.obj);
+            return;
+        default:
+            super.handleMessage(msg);
+            return;
+        }
+    }
+
+    public void startLongPress(final Key key) {
+        cancelLongPress();
+        final Message longPressMessage = obtainMessage(MSG_LONG_PRESS, key);
+        sendMessageDelayed(longPressMessage, mConfigAccessibilityLongPressTimeout);
+    }
+
+    public void cancelLongPress() {
+        removeMessages(MSG_LONG_PRESS);
+    }
+}
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
index 1ee586cce0188f774364b48d91f4e060371191a1..d67d9dc4bb0213052f3ef2fa58741d57a8474205 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
@@ -33,14 +33,29 @@ import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.PointerTracker;
 
+/**
+ * This class represents a delegate that can be registered in a class that extends
+ * {@link KeyboardView} to enhance accessibility support via composition rather via inheritance.
+ *
+ * To implement accessibility mode, the target keyboard view has to:<p>
+ * - Call {@link #setKeyboard(Keyboard)} when a new keyboard is set to the keyboard view.
+ * - Dispatch a hover event by calling {@link #onHoverEnter(MotionEvent)}.
+ *
+ * @param <KV> The keyboard view class type.
+ */
 public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
         extends AccessibilityDelegateCompat {
+    private static final String TAG = KeyboardAccessibilityDelegate.class.getSimpleName();
+    protected static final boolean DEBUG_HOVER = false;
+
     protected final KV mKeyboardView;
     protected final KeyDetector mKeyDetector;
     private Keyboard mKeyboard;
     private KeyboardAccessibilityNodeProvider mAccessibilityNodeProvider;
     private Key mLastHoverKey;
 
+    public static final int HOVER_EVENT_POINTER_ID = 0;
+
     public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) {
         super();
         mKeyboardView = keyboardView;
@@ -180,8 +195,11 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
      */
     protected void onHoverEnter(final MotionEvent event) {
         final Key key = getHoverKeyOf(event);
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverEnter: key=" + key);
+        }
         if (key != null) {
-            onHoverEnterKey(key);
+            onHoverEnterTo(key);
         }
         setLastHoverKey(key);
     }
@@ -196,14 +214,14 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
         final Key key = getHoverKeyOf(event);
         if (key != lastKey) {
             if (lastKey != null) {
-                onHoverExitKey(lastKey);
+                onHoverExitFrom(lastKey);
             }
             if (key != null) {
-                onHoverEnterKey(key);
+                onHoverEnterTo(key);
             }
         }
         if (key != null) {
-            onHoverMoveKey(key);
+            onHoverMoveWithin(key);
         }
         setLastHoverKey(key);
     }
@@ -215,15 +233,18 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
      */
     protected void onHoverExit(final MotionEvent event) {
         final Key lastKey = getLastHoverKey();
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey);
+        }
         if (lastKey != null) {
-            onHoverExitKey(lastKey);
+            onHoverExitFrom(lastKey);
         }
         final Key key = getHoverKeyOf(event);
         // Make sure we're not getting an EXIT event because the user slid
         // off the keyboard area, then force a key press.
         if (key != null) {
             onRegisterHoverKey(key, event);
-            onHoverExitKey(key);
+            onHoverExitFrom(key);
         }
         setLastHoverKey(null);
     }
@@ -235,6 +256,9 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
      * @param event A hover exit event that triggers key registering.
      */
     protected void onRegisterHoverKey(final Key key, final MotionEvent event) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onRegisterHoverKey: key=" + key);
+        }
         simulateTouchEvent(MotionEvent.ACTION_DOWN, event);
         simulateTouchEvent(MotionEvent.ACTION_UP, event);
     }
@@ -274,7 +298,10 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
      *
      * @param key The currently hovered key.
      */
-    protected void onHoverEnterKey(final Key key) {
+    protected void onHoverEnterTo(final Key key) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverEnterTo: key=" + key);
+        }
         key.onPressed();
         mKeyboardView.invalidateKey(key);
         final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider();
@@ -287,14 +314,17 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
      *
      * @param key The currently hovered key.
      */
-    protected void onHoverMoveKey(final Key key) { }
+    protected void onHoverMoveWithin(final Key key) { }
 
     /**
      * Handles a hover exit event on a key.
      *
      * @param key The currently hovered key.
      */
-    protected void onHoverExitKey(final Key key) {
+    protected void onHoverExitFrom(final Key key) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverExitFrom: key=" + key);
+        }
         key.onReleased();
         mKeyboardView.invalidateKey(key);
         final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider();
diff --git a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
index ec6bb0156c86088a3c51231241b511002dd84c4e..4fdf5b8fa7863a7718454593a4a77c1da5dd172a 100644
--- a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
+++ b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
@@ -17,17 +17,29 @@
 package com.android.inputmethod.accessibility;
 
 import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
 import android.util.SparseIntArray;
+import android.view.MotionEvent;
 
+import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
+/**
+ * This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance
+ * accessibility support via composition rather via inheritance.
+ */
 public final class MainKeyboardAccessibilityDelegate
-        extends KeyboardAccessibilityDelegate<MainKeyboardView> {
+        extends KeyboardAccessibilityDelegate<MainKeyboardView>
+        implements AccessibilityLongPressTimer.LongPressTimerCallback {
+    private static final String TAG = MainKeyboardAccessibilityDelegate.class.getSimpleName();
+
     /** Map of keyboard modes to resource IDs. */
     private static final SparseIntArray KEYBOARD_MODE_RES_IDS = new SparseIntArray();
 
@@ -46,10 +58,15 @@ public final class MainKeyboardAccessibilityDelegate
     /** The most recently set keyboard mode. */
     private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN;
     private static final int KEYBOARD_IS_HIDDEN = -1;
+    private boolean mShouldIgnoreOnRegisterHoverKey;
+
+    private final AccessibilityLongPressTimer mAccessibilityLongPressTimer;
 
     public MainKeyboardAccessibilityDelegate(final MainKeyboardView mainKeyboardView,
             final KeyDetector keyDetector) {
         super(mainKeyboardView, keyDetector);
+        mAccessibilityLongPressTimer = new AccessibilityLongPressTimer(
+                this /* callback */, mainKeyboardView.getContext());
     }
 
     /**
@@ -172,4 +189,63 @@ public final class MainKeyboardAccessibilityDelegate
     private void announceKeyboardHidden() {
         sendWindowStateChanged(R.string.announce_keyboard_hidden);
     }
+
+    @Override
+    protected void onRegisterHoverKey(final Key key, final MotionEvent event) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onRegisterHoverKey: key=" + key + " ignore="
+                    + mShouldIgnoreOnRegisterHoverKey);
+        }
+        if (!mShouldIgnoreOnRegisterHoverKey) {
+            super.onRegisterHoverKey(key, event);
+        }
+        mShouldIgnoreOnRegisterHoverKey = false;
+    }
+
+    @Override
+    protected void onHoverEnterTo(final Key key) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverEnterTo: key=" + key);
+        }
+        mAccessibilityLongPressTimer.cancelLongPress();
+        super.onHoverEnterTo(key);
+        if (key.isLongPressEnabled()) {
+            mAccessibilityLongPressTimer.startLongPress(key);
+        }
+    }
+
+    protected void onHoverExitFrom(final Key key) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverExitFrom: key=" + key);
+        }
+        mAccessibilityLongPressTimer.cancelLongPress();
+        super.onHoverExitFrom(key);
+    }
+
+    @Override
+    public void onLongPressed(final Key key) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onLongPressed: key=" + key);
+        }
+        final PointerTracker tracker = PointerTracker.getPointerTracker(HOVER_EVENT_POINTER_ID);
+        final long eventTime = SystemClock.uptimeMillis();
+        final int x = key.getHitBox().centerX();
+        final int y = key.getHitBox().centerY();
+        final MotionEvent downEvent = MotionEvent.obtain(
+                eventTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */);
+        // Inject a fake down event to {@link PointerTracker} to handle a long press correctly.
+        tracker.processMotionEvent(downEvent, mKeyDetector);
+        // The above fake down event triggers an unnecessary long press timer that should be
+        // canceled.
+        tracker.cancelLongPressTimer();
+        downEvent.recycle();
+        // Invoke {@link MainKeyboardView#onLongPress(PointerTracker)} as if a long press timeout
+        // has passed.
+        mKeyboardView.onLongPress(tracker);
+        // If {@link Key#hasNoPanelAutoMoreKeys()} is true (such as "0 +" key on the phone layout)
+        // or a key invokes IME switcher dialog, we should just ignore the next
+        // {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether
+        // {@link PointerTracker} is in operation or not.
+        mShouldIgnoreOnRegisterHoverKey = !tracker.isInOperation();
+    }
 }
diff --git a/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a56c5d2a018395af0babf16ec8116179c509a47
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 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.accessibility;
+
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
+import com.android.inputmethod.latin.Constants;
+
+/**
+ * This class represents a delegate that can be registered in {@link MoreKeysKeyboardView} to
+ * enhance accessibility support via composition rather via inheritance.
+ */
+public class MoreKeysKeyboardAccessibilityDelegate
+        extends KeyboardAccessibilityDelegate<MoreKeysKeyboardView> {
+    private static final String TAG = MoreKeysKeyboardAccessibilityDelegate.class.getSimpleName();
+
+    private final Rect mMoreKeysKeyboardValidBounds = new Rect();
+    private static final int CLOSING_INSET_IN_PIXEL = 1;
+    private int mOpenAnnounceResId;
+    private int mCloseAnnounceResId;
+
+    public MoreKeysKeyboardAccessibilityDelegate(final MoreKeysKeyboardView moreKeysKeyboardView,
+            final KeyDetector keyDetector) {
+        super(moreKeysKeyboardView, keyDetector);
+    }
+
+    public void setOpenAnnounce(final int resId) {
+        mOpenAnnounceResId = resId;
+    }
+
+    public void setCloseAnnounce(final int resId) {
+        mCloseAnnounceResId = resId;
+    }
+
+    public void onShowMoreKeysKeyboard() {
+        sendWindowStateChanged(mOpenAnnounceResId);
+    }
+
+    @Override
+    protected void onHoverEnter(final MotionEvent event) {
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverEnter: key=" + getHoverKeyOf(event));
+        }
+        super.onHoverEnter(event);
+        final int actionIndex = event.getActionIndex();
+        final int x = (int)event.getX(actionIndex);
+        final int y = (int)event.getY(actionIndex);
+        final int pointerId = event.getPointerId(actionIndex);
+        final long eventTime = event.getEventTime();
+        mKeyboardView.onDownEvent(x, y, pointerId, eventTime);
+    }
+
+    @Override
+    protected void onHoverMove(final MotionEvent event) {
+        super.onHoverMove(event);
+        final int actionIndex = event.getActionIndex();
+        final int x = (int)event.getX(actionIndex);
+        final int y = (int)event.getY(actionIndex);
+        final int pointerId = event.getPointerId(actionIndex);
+        final long eventTime = event.getEventTime();
+        mKeyboardView.onMoveEvent(x, y, pointerId, eventTime);
+    }
+
+    @Override
+    protected void onHoverExit(final MotionEvent event) {
+        final Key lastKey = getLastHoverKey();
+        if (DEBUG_HOVER) {
+            Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey);
+        }
+        if (lastKey != null) {
+            super.onHoverExitFrom(lastKey);
+        }
+        setLastHoverKey(null);
+        final int actionIndex = event.getActionIndex();
+        final int x = (int)event.getX(actionIndex);
+        final int y = (int)event.getY(actionIndex);
+        final int pointerId = event.getPointerId(actionIndex);
+        final long eventTime = event.getEventTime();
+        // A hover exit event at one pixel width or height area on the edges of more keys keyboard
+        // are treated as closing.
+        mMoreKeysKeyboardValidBounds.set(0, 0, mKeyboardView.getWidth(), mKeyboardView.getHeight());
+        mMoreKeysKeyboardValidBounds.inset(CLOSING_INSET_IN_PIXEL, CLOSING_INSET_IN_PIXEL);
+        if (mMoreKeysKeyboardValidBounds.contains(x, y)) {
+            // Invoke {@link MoreKeysKeyboardView#onUpEvent(int,int,int,long)} as if this hover
+            // exit event selects a key.
+            mKeyboardView.onUpEvent(x, y, pointerId, eventTime);
+            mKeyboardView.dismissMoreKeysPanel();
+            return;
+        }
+        // Close the more keys keyboard.
+        mKeyboardView.onMoveEvent(
+                Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, pointerId, eventTime);
+        sendWindowStateChanged(mCloseAnnounceResId);
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 50c82e5f7e97d55597dd57730d0b96fb82a79900..adb92d335963029620b10504bbec848bc019a8c8 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -17,12 +17,13 @@
 package com.android.inputmethod.keyboard;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.utils.CoordinateUtils;
@@ -34,7 +35,7 @@ import com.android.inputmethod.latin.utils.CoordinateUtils;
 public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
     private final int[] mCoordinates = CoordinateUtils.newInstance();
 
-    protected final KeyDetector mKeyDetector;
+    protected KeyDetector mKeyDetector;
     private Controller mController = EMPTY_CONTROLLER;
     protected KeyboardActionListener mListener;
     private int mOriginX;
@@ -43,6 +44,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
 
     private int mActivePointerId;
 
+    private MoreKeysKeyboardAccessibilityDelegate mAccessibilityDelegate;
+
     public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
     }
@@ -50,10 +53,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
     public MoreKeysKeyboardView(final Context context, final AttributeSet attrs,
             final int defStyle) {
         super(context, attrs, defStyle);
-
-        final Resources res = context.getResources();
-        mKeyDetector = new MoreKeysDetector(
-                res.getDimension(R.dimen.config_more_keys_keyboard_slide_allowance));
+        mKeyDetector = new MoreKeysDetector(getResources().getDimension(
+                R.dimen.config_more_keys_keyboard_slide_allowance));
     }
 
     @Override
@@ -71,8 +72,23 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
     @Override
     public void setKeyboard(final Keyboard keyboard) {
         super.setKeyboard(keyboard);
-        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
-                -getPaddingTop() + getVerticalCorrection());
+        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+            // With accessibility mode on, any hover event outside {@link MoreKeysKeyboardView} is
+            // discarded at {@link InputView#dispatchHoverEvent(MotionEvent)}. Because only a hover
+            // event that is on this view is dispatched by the platform, we should use a
+            // {@link KeyDetector} that has no sliding allowance and no hysteresis.
+            mKeyDetector = new KeyDetector();
+            mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate(this, mKeyDetector);
+            mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_keys_keyboard);
+            mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard);
+            mAccessibilityDelegate.setKeyboard(keyboard);
+        } else {
+            mKeyDetector = new MoreKeysDetector(getResources().getDimension(
+                    R.dimen.config_more_keys_keyboard_slide_allowance));
+            mAccessibilityDelegate = null;
+        }
+        mKeyDetector.setKeyboard(
+                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
     }
 
     @Override
@@ -98,6 +114,10 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
         mOriginX = x + container.getPaddingLeft();
         mOriginY = y + container.getPaddingTop();
         controller.onShowMoreKeysPanel(this);
+        final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
+        if (accessibilityDelegate != null) {
+            accessibilityDelegate.onShowMoreKeysKeyboard();
+        }
     }
 
     /**
@@ -228,6 +248,18 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
         return true;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onHoverEvent(final MotionEvent event) {
+        final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
+        if (accessibilityDelegate != null) {
+            return accessibilityDelegate.onHoverEvent(event);
+        }
+        return super.onHoverEvent(event);
+    }
+
     private View getContainerView() {
         return (View)getParent();
     }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index ff6fd86d0e0ee3b6d31ed671afab2e41972e530f..b6905bc1c57c469ea601d4480338a0c756798c13 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -1078,6 +1078,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
         mIsTrackingForActionDisabled = true;
     }
 
+    public boolean isInOperation() {
+        return !mIsTrackingForActionDisabled;
+    }
+
+    public void cancelLongPressTimer() {
+        sTimerProxy.cancelLongPressTimerOf(this);
+    }
+
     public void onLongPressed() {
         resetKeySelectionByDraggingFinger();
         cancelTrackingForAction();
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index ea7859e604939a08dbe3b42f4db4e148ea68ebf5..0801cfa88c99c671fdfd6a46cb16e80cdbee1899 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -23,12 +23,14 @@ import android.view.MotionEvent;
 import android.view.View;
 import android.widget.LinearLayout;
 
+import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.latin.suggestions.MoreSuggestionsView;
 import com.android.inputmethod.latin.suggestions.SuggestionStripView;
 
 public final class InputView extends LinearLayout {
     private final Rect mInputViewRect = new Rect();
+    private MainKeyboardView mMainKeyboardView;
     private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
     private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
     private MotionEventForwarder<?, ?> mActiveForwarder;
@@ -41,18 +43,28 @@ public final class InputView extends LinearLayout {
     protected void onFinishInflate() {
         final SuggestionStripView suggestionStripView =
                 (SuggestionStripView)findViewById(R.id.suggestion_strip_view);
-        final MainKeyboardView mainKeyboardView =
-                (MainKeyboardView)findViewById(R.id.keyboard_view);
+        mMainKeyboardView = (MainKeyboardView)findViewById(R.id.keyboard_view);
         mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder(
-                mainKeyboardView, suggestionStripView);
+                mMainKeyboardView, suggestionStripView);
         mMoreSuggestionsViewCanceler = new MoreSuggestionsViewCanceler(
-                mainKeyboardView, suggestionStripView);
+                mMainKeyboardView, suggestionStripView);
     }
 
     public void setKeyboardTopPadding(final int keyboardTopPadding) {
         mKeyboardTopPaddingForwarder.setKeyboardTopPadding(keyboardTopPadding);
     }
 
+    @Override
+    protected boolean dispatchHoverEvent(final MotionEvent event) {
+        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()
+                && mMainKeyboardView.isShowingMoreKeysPanel()) {
+            // With accessibility mode on, discard hover events while a more keys keyboard is shown.
+            // The {@link MoreKeysKeyboard} receives hover events directly from the platform.
+            return true;
+        }
+        return super.dispatchHoverEvent(event);
+    }
+
     @Override
     public boolean onInterceptTouchEvent(final MotionEvent me) {
         final Rect rect = mInputViewRect;