diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index dfc70ef416af5..05444f628ffee 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -28,6 +28,7 @@ import android.graphics.Paint.Align;
import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard.Key;
+import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
@@ -39,6 +40,8 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.PopupWindow;
import android.widget.TextView;
@@ -175,7 +178,6 @@ public class KeyboardView extends View implements View.OnClickListener {
private boolean mShowTouchPoints = true;
private int mPopupPreviewX;
private int mPopupPreviewY;
- private int mWindowY;
private int mLastX;
private int mLastY;
@@ -242,7 +244,11 @@ public class KeyboardView extends View implements View.OnClickListener {
private boolean mKeyboardChanged;
/** The canvas for the above mutable keyboard bitmap */
private Canvas mCanvas;
-
+ /** The accessibility manager for accessibility support */
+ private AccessibilityManager mAccessibilityManager;
+ /** The audio manager for accessibility support */
+ private AudioManager mAudioManager;
+
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -362,6 +368,10 @@ public class KeyboardView extends View implements View.OnClickListener {
mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density);
mDisambiguateSwipe = getResources().getBoolean(
com.android.internal.R.bool.config_swipeDisambiguation);
+
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
resetMultiTap();
initGestureDetector();
}
@@ -835,12 +845,16 @@ public class KeyboardView extends View implements View.OnClickListener {
final Key[] keys = mKeys;
if (oldKeyIndex != mCurrentKeyIndex) {
if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) {
- keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY);
+ Key oldKey = keys[oldKeyIndex];
+ oldKey.onReleased(mCurrentKeyIndex == NOT_A_KEY);
invalidateKey(oldKeyIndex);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT, oldKey.codes[0]);
}
if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) {
- keys[mCurrentKeyIndex].onPressed();
+ Key newKey = keys[mCurrentKeyIndex];
+ newKey.onPressed();
invalidateKey(mCurrentKeyIndex);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, newKey.codes[0]);
}
}
// If key changed and preview is on ...
@@ -940,6 +954,47 @@ public class KeyboardView extends View implements View.OnClickListener {
mPreviewText.setVisibility(VISIBLE);
}
+ private void sendAccessibilityEvent(int eventType, int code) {
+ if (mAccessibilityManager.isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ onInitializeAccessibilityEvent(event);
+ // Add text only if headset is used to avoid leaking passwords.
+ if (mAudioManager.isBluetoothA2dpOn() || mAudioManager.isWiredHeadsetOn()) {
+ String text = null;
+ switch (code) {
+ case Keyboard.KEYCODE_ALT:
+ text = mContext.getString(R.string.keyboardview_keycode_alt);
+ break;
+ case Keyboard.KEYCODE_CANCEL:
+ text = mContext.getString(R.string.keyboardview_keycode_cancel);
+ break;
+ case Keyboard.KEYCODE_DELETE:
+ text = mContext.getString(R.string.keyboardview_keycode_delete);
+ break;
+ case Keyboard.KEYCODE_DONE:
+ text = mContext.getString(R.string.keyboardview_keycode_done);
+ break;
+ case Keyboard.KEYCODE_MODE_CHANGE:
+ text = mContext.getString(R.string.keyboardview_keycode_mode_change);
+ break;
+ case Keyboard.KEYCODE_SHIFT:
+ text = mContext.getString(R.string.keyboardview_keycode_shift);
+ break;
+ case '\n':
+ text = mContext.getString(R.string.keyboardview_keycode_enter);
+ break;
+ default:
+ text = String.valueOf((char) code);
+ }
+ event.getText().add(text);
+ } else {
+ event.getText().add(mContext.getString(
+ R.string.keyboard_headset_required_to_hear_password));
+ }
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+ }
+
/**
* Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
* because the keyboard renders the keys to an off-screen buffer and an invalidate() only
@@ -1074,11 +1129,49 @@ public class KeyboardView extends View implements View.OnClickListener {
return false;
}
- private long mOldEventTime;
- private boolean mUsedVelocity;
+ @Override
+ protected boolean dispatchHoverEvent(MotionEvent event) {
+ // If touch exploring is enabled we ignore touch events and transform
+ // the stream of hover events as touch events. This allows one consistent
+ // event stream to drive the keyboard since during touch exploring the
+ // first touch generates only hover events and tapping on the same
+ // location generates hover and touch events.
+ if (mAccessibilityManager.isEnabled()
+ && mAccessibilityManager.isTouchExplorationEnabled()
+ && event.getPointerCount() == 1) {
+ final int action = event.getAction();
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ event.setAction(MotionEvent.ACTION_DOWN);
+ break;
+ case MotionEvent.ACTION_HOVER_MOVE:
+ event.setAction(MotionEvent.ACTION_MOVE);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ event.setAction(MotionEvent.ACTION_UP);
+ break;
+ }
+ onTouchEventInternal(event);
+ return true;
+ }
+ return super.dispatchHoverEvent(event);
+ }
@Override
- public boolean onTouchEvent(MotionEvent me) {
+ public boolean onTouchEvent(MotionEvent event) {
+ // If touch exploring is enabled we ignore touch events and transform
+ // the stream of hover events as touch events. This allows one consistent
+ // event stream to drive the keyboard since during touch exploring the
+ // first touch generates only hover events and tapping on the same
+ // location generates hover and touch events.
+ if (mAccessibilityManager.isEnabled()
+ && mAccessibilityManager.isTouchExplorationEnabled()) {
+ return true;
+ }
+ return onTouchEventInternal(event);
+ }
+
+ private boolean onTouchEventInternal(MotionEvent me) {
// Convert multi-pointer up/down events to single up/down events to
// deal with the typical multi-pointer behavior of two-thumb typing
final int pointerCount = me.getPointerCount();
@@ -1126,7 +1219,6 @@ public class KeyboardView extends View implements View.OnClickListener {
touchY += mVerticalCorrection;
final int action = me.getAction();
final long eventTime = me.getEventTime();
- mOldEventTime = eventTime;
int keyIndex = getKeyIndices(touchX, touchY, null);
mPossiblePoly = possiblePoly;
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6aff54e5128f7..2548eb5d2e0be 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3068,6 +3068,26 @@
not pressed
+
+
+ Alt
+
+ Cancel
+
+ Delete
+
+ Done
+
+ Mode change
+
+ Shift
+
+ Enter
+
+
+ Key. Headset required to hear
+ keys while typing a password.
+
Navigate home