AccessibleKeyboardViewProxy.java revision 586a15c3f0d44590a5162e0ab4c3c52511f13f26
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; 32f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.LatinKeyboardBaseView; 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; 43f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka private LatinKeyboardBaseView mView; 445ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private AccessibleKeyboardActionListener mListener; 4587d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette private FlickGestureDetector mGestureDetector; 465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; 485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private int mLastX = -1; 495ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private int mLastY = -1; 505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public static void init(Context context, SharedPreferences prefs) { 525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette sInstance.initInternal(context, prefs); 535ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance(); 545ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public static AccessibleKeyboardViewProxy getInstance() { 575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return sInstance; 585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 595ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 60f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka public static void setView(LatinKeyboardBaseView view) { 615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette sInstance.mView = view; 625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 645ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private AccessibleKeyboardViewProxy() { 655ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // Not publicly instantiable. 665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 675ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 685ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private void initInternal(Context context, SharedPreferences prefs) { 695ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final Paint paint = new Paint(); 705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setTextAlign(Paint.Align.LEFT); 715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setTextSize(14.0f); 725ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setAntiAlias(true); 735ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette paint.setColor(Color.YELLOW); 745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 7587d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette mGestureDetector = new KeyboardFlickGestureDetector(context); 765ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mScaledEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop(); 775ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 785ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 795ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event, 805ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette PointerTracker tracker) { 815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (mView == null) { 825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette Log.e(TAG, "No keyboard view set!"); 835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 865ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette switch (event.getEventType()) { 875ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER: 885ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final Key key = tracker.getKey(mLastHoverKeyIndex); 895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 905ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (key == null) 915ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette break; 925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final CharSequence description = KeyCodeDescriptionMapper.getInstance() 945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key); 955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 965ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (description == null) 975ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 985ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 995ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette event.getText().add(description); 1005ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1015ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette break; 1025ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1035ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1045ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1055ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1065ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1075ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 108586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * Receives hover events when accessibility is turned on in SDK versions ICS 109586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * and higher. 1105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 1115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @param event The hover event. 1125ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @return {@code true} if the event is handled 1135ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 114586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) { 11587d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette if (mGestureDetector.onHoverEvent(event, this, tracker)) 11687d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette return true; 11787d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette 11887d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette return onHoverEventInternal(event, tracker); 1195ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1215ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public boolean dispatchTouchEvent(MotionEvent event) { 1225ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // Since touch exploration translates hover double-tap to a regular 1235ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // single-tap, we're going to drop non-touch exploration events. 1245ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (!AccessibilityUtils.getInstance().isTouchExplorationEvent(event)) 1255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1265ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1275ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 1285ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1295ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1305ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 1315ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Handles touch exploration events when Accessibility is turned on. 1325ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 1335ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @param event The touch exploration hover event. 1345ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @return {@code true} if the event was handled 1355ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 13687d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette /*package*/ boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) { 1375ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int x = (int) event.getX(); 1385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int y = (int) event.getY(); 1395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette switch (event.getAction()) { 1415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case MotionEventCompatUtils.ACTION_HOVER_ENTER: 1425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case MotionEventCompatUtils.ACTION_HOVER_MOVE: 1435ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int keyIndex = tracker.getKeyIndexOn(x, y); 1445ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1455ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (keyIndex != mLastHoverKeyIndex) { 1465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false); 1475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastHoverKeyIndex = keyIndex; 1485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastX = x; 1495ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastY = y; 1505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyHoverEvent(tracker, mLastHoverKeyIndex, true); 1515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1535ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1545ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette case MotionEventCompatUtils.ACTION_HOVER_EXIT: 1555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int width = mView.getWidth(); 1565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int height = mView.getHeight(); 1575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (x < mScaledEdgeSlop || y < mScaledEdgeSlop || x >= (width - mScaledEdgeSlop) 1595ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette || y >= (height - mScaledEdgeSlop)) { 1605ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false); 1615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; 1625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastX = -1; 1635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mLastY = -1; 1645ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } else if (mLastHoverKeyIndex != KeyDetector.NOT_A_KEY) { 1655ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette fireKeyPressEvent(tracker, mLastX, mLastY, event.getEventTime()); 1665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1675ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1685ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return true; 1695ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return false; 1725ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1735ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private void fireKeyHoverEvent(PointerTracker tracker, int keyIndex, boolean entering) { 1755ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (mListener == null) { 1765ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette Log.e(TAG, "No accessible keyboard action listener set!"); 1775ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1785ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1795ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1805ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (mView == null) { 1815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette Log.e(TAG, "No keyboard view set!"); 1825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (keyIndex == KeyDetector.NOT_A_KEY) 1865ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1875ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1885ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final Key key = tracker.getKey(keyIndex); 1895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1905ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (key == null) 1915ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (entering) { 1945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mListener.onHoverEnter(key.mCode); 1955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER); 1965ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } else { 1975ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mListener.onHoverExit(key.mCode); 1985ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_EXIT); 1995ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 2005ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 2015ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 2025ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private void fireKeyPressEvent(PointerTracker tracker, int x, int y, long eventTime) { 2030efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka tracker.onDownEvent(x, y, eventTime, mView); 204906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka tracker.onUpEvent(x, y, eventTime + DELAY_KEY_PRESS); 2055ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 20687d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette 20787d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette private class KeyboardFlickGestureDetector extends FlickGestureDetector { 20887d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette public KeyboardFlickGestureDetector(Context context) { 20987d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette super(context); 21087d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette } 21187d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette 21287d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette @Override 21387d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette public boolean onFlick(MotionEvent e1, MotionEvent e2, int direction) { 21487d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette if (mListener != null) { 21587d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette mListener.onFlickGesture(direction); 21687d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette } 21787d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette return true; 21887d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette } 21987d7929d142f7c5f1937e12d6fd32a43ab00740eAlan Viverette } 2205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette} 221