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