AccessibleKeyboardViewProxy.java revision e7759091ddb5ec18268945d70d9212195bf6497b
15ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette/* 25ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Copyright (C) 2011 The Android Open Source Project 35ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 45ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License"); you may not 55ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * use this file except in compliance with the License. You may obtain a copy of 65ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * the License at 75ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 85ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * http://www.apache.org/licenses/LICENSE-2.0 95ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Unless required by applicable law or agreed to in writing, software 115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 125ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 135ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * License for the specific language governing permissions and limitations under 145ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * the License. 155ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 165ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 175ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverettepackage com.android.inputmethod.accessibility; 185ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 195ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.content.Context; 205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.content.SharedPreferences; 215ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.graphics.Color; 225ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.graphics.Paint; 235ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.util.Log; 245ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.view.MotionEvent; 255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.view.ViewConfiguration; 265ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.view.accessibility.AccessibilityEvent; 275ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 285ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.compat.AccessibilityEventCompatUtils; 295ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.compat.MotionEventCompatUtils; 30e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaokaimport com.android.inputmethod.keyboard.Key; 315ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.keyboard.KeyDetector; 325ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.keyboard.KeyboardView; 335ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.keyboard.PointerTracker; 345ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 355ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverettepublic class AccessibleKeyboardViewProxy { 365ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName(); 375ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy(); 385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // Delay in milliseconds between key press DOWN and UP events 405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final long DELAY_KEY_PRESS = 10; 415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private int mScaledEdgeSlop; 435ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private KeyboardView mView; 445ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private AccessibleKeyboardActionListener mListener; 455ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; 475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private int mLastX = -1; 485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private int mLastY = -1; 495ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public static void init(Context context, SharedPreferences prefs) { 515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette sInstance.initInternal(context, prefs); 525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance(); 535ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 545ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public static AccessibleKeyboardViewProxy getInstance() { 565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return sInstance; 575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 595ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public static void setView(KeyboardView view) { 605ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette sInstance.mView = view; 615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private AccessibleKeyboardViewProxy() { 645ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // Not publicly instantiable. 655ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 675ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private void initInternal(Context context, SharedPreferences prefs) { 685ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final Paint paint = new Paint(); 695ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setTextAlign(Paint.Align.LEFT); 705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setTextSize(14.0f); 715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setAntiAlias(true); 725ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setColor(Color.YELLOW); 735ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mScaledEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop(); 755ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 765ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 775ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event, 785ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette PointerTracker tracker) { 795ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (mView == null) { 805ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette Log.e(TAG, "No keyboard view set!"); 815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette switch (event.getEventType()) { 855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER: 865ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final Key key = tracker.getKey(mLastHoverKeyIndex); 875ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 885ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (key == null) 895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette break; 905ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 915ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final CharSequence description = KeyCodeDescriptionMapper.getInstance() 925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key); 935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (description == null) 955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 965ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 975ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette event.getText().add(description); 985ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 995ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette break; 1005ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1015ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1025ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1035ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1045ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1055ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 1065ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Receives hover events when accessibility is turned on in API > 11. In 1075ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * earlier API levels, events are manually routed from onTouchEvent. 1085ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 1095ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @param event The hover event. 1105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @return {@code true} if the event is handled 1115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 1125ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public boolean onHoverEvent(MotionEvent event, PointerTracker tracker) { 1135ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return onTouchExplorationEvent(event, tracker); 1145ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1155ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1165ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public boolean dispatchTouchEvent(MotionEvent event) { 1175ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // Since touch exploration translates hover double-tap to a regular 1185ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // single-tap, we're going to drop non-touch exploration events. 1195ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (!AccessibilityUtils.getInstance().isTouchExplorationEvent(event)) 1205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1215ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1225ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 1235ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1245ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 1265ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Handles touch exploration events when Accessibility is turned on. 1275ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 1285ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @param event The touch exploration hover event. 1295ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @return {@code true} if the event was handled 1305ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 1315ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private boolean onTouchExplorationEvent(MotionEvent event, PointerTracker tracker) { 1325ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int x = (int) event.getX(); 1335ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int y = (int) event.getY(); 1345ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1355ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette switch (event.getAction()) { 1365ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case MotionEventCompatUtils.ACTION_HOVER_ENTER: 1375ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case MotionEventCompatUtils.ACTION_HOVER_MOVE: 1385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int keyIndex = tracker.getKeyIndexOn(x, y); 1395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (keyIndex != mLastHoverKeyIndex) { 1415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false); 1425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastHoverKeyIndex = keyIndex; 1435ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastX = x; 1445ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastY = y; 1455ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyHoverEvent(tracker, mLastHoverKeyIndex, true); 1465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1495ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case MotionEventCompatUtils.ACTION_HOVER_EXIT: 1505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int width = mView.getWidth(); 1515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int height = mView.getHeight(); 1525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1535ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (x < mScaledEdgeSlop || y < mScaledEdgeSlop || x >= (width - mScaledEdgeSlop) 1545ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette || y >= (height - mScaledEdgeSlop)) { 1555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false); 1565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; 1575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastX = -1; 1585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastY = -1; 1595ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } else if (mLastHoverKeyIndex != KeyDetector.NOT_A_KEY) { 1605ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyPressEvent(tracker, mLastX, mLastY, event.getEventTime()); 1615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1645ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1655ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 1675ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1685ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1695ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private void fireKeyHoverEvent(PointerTracker tracker, int keyIndex, boolean entering) { 1705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (mListener == null) { 1715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette Log.e(TAG, "No accessible keyboard action listener set!"); 1725ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1735ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1755ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (mView == null) { 1765ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette Log.e(TAG, "No keyboard view set!"); 1775ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1785ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1795ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1805ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (keyIndex == KeyDetector.NOT_A_KEY) 1815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final Key key = tracker.getKey(keyIndex); 1845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (key == null) 1865ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1875ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1885ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (entering) { 1895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mListener.onHoverEnter(key.mCode); 1905ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER); 1915ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } else { 1925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mListener.onHoverExit(key.mCode); 1935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_EXIT); 1945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1965ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1975ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private void fireKeyPressEvent(PointerTracker tracker, int x, int y, long eventTime) { 1985ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette tracker.onDownEvent(x, y, eventTime, null); 1995ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette tracker.onUpEvent(x, y, eventTime + DELAY_KEY_PRESS, null); 2005ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 2015ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette} 202