From e8b8e1aed046711b41804de1670e58c7a8a9d6ad Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Sun, 21 Aug 2011 15:06:28 -0700 Subject: [PATCH] Adding accessibility support to the PIN lock screen. 1. The password lock screen is accessible and with this change the PIN lock screen is accessibile as well. This is enough to cover the enterprise use case of imposed lock of the deivce. we will hide the options for pattern since it is hard for use by a blind person. We may reconsider this for subsequent releases. bug:4978246 Change-Id: I069f8ebe1ff7ea1591cab42ea580f00f3d31b2e6 --- .../inputmethodservice/KeyboardView.java | 108 ++++++++++++++++-- core/res/res/values/strings.xml | 20 ++++ 2 files changed, 120 insertions(+), 8 deletions(-) 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