KeyboardAccessibilityNodeProvider.java revision 067689c57d353e4e37e0457989c6c2686977df9e
19a81ce92c381007affe6bb2310bf94c9856eaae1alanv/*
29a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Copyright (C) 2012 The Android Open Source Project
39a81ce92c381007affe6bb2310bf94c9856eaae1alanv *
49a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Licensed under the Apache License, Version 2.0 (the "License");
59a81ce92c381007affe6bb2310bf94c9856eaae1alanv * you may not use this file except in compliance with the License.
69a81ce92c381007affe6bb2310bf94c9856eaae1alanv * You may obtain a copy of the License at
79a81ce92c381007affe6bb2310bf94c9856eaae1alanv *
89a81ce92c381007affe6bb2310bf94c9856eaae1alanv *      http://www.apache.org/licenses/LICENSE-2.0
99a81ce92c381007affe6bb2310bf94c9856eaae1alanv *
109a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Unless required by applicable law or agreed to in writing, software
119a81ce92c381007affe6bb2310bf94c9856eaae1alanv * distributed under the License is distributed on an "AS IS" BASIS,
129a81ce92c381007affe6bb2310bf94c9856eaae1alanv * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139a81ce92c381007affe6bb2310bf94c9856eaae1alanv * See the License for the specific language governing permissions and
149a81ce92c381007affe6bb2310bf94c9856eaae1alanv * limitations under the License.
159a81ce92c381007affe6bb2310bf94c9856eaae1alanv */
169a81ce92c381007affe6bb2310bf94c9856eaae1alanv
179a81ce92c381007affe6bb2310bf94c9856eaae1alanvpackage com.android.inputmethod.accessibility;
189a81ce92c381007affe6bb2310bf94c9856eaae1alanv
199a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.graphics.Rect;
209a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.inputmethodservice.InputMethodService;
21f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.os.Bundle;
22f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.os.SystemClock;
239a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.ViewCompat;
24f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.support.v4.view.accessibility.AccessibilityEventCompat;
259a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
269a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
279a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.support.v4.view.accessibility.AccessibilityRecordCompat;
289a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.util.Log;
299a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.util.SparseArray;
30f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanvimport android.view.MotionEvent;
319a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.View;
329a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.ViewTreeObserver.OnGlobalLayoutListener;
339a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.accessibility.AccessibilityEvent;
349a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport android.view.inputmethod.EditorInfo;
359a81ce92c381007affe6bb2310bf94c9856eaae1alanv
369a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Key;
379a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.Keyboard;
389a81ce92c381007affe6bb2310bf94c9856eaae1alanvimport com.android.inputmethod.keyboard.KeyboardView;
399a81ce92c381007affe6bb2310bf94c9856eaae1alanv
409a81ce92c381007affe6bb2310bf94c9856eaae1alanv/**
419a81ce92c381007affe6bb2310bf94c9856eaae1alanv * Exposes a virtual view sub-tree for {@link KeyboardView} and generates
429a81ce92c381007affe6bb2310bf94c9856eaae1alanv * {@link AccessibilityEvent}s for individual {@link Key}s.
439a81ce92c381007affe6bb2310bf94c9856eaae1alanv * <p>
449a81ce92c381007affe6bb2310bf94c9856eaae1alanv * A virtual sub-tree is composed of imaginary {@link View}s that are reported
459a81ce92c381007affe6bb2310bf94c9856eaae1alanv * as a part of the view hierarchy for accessibility purposes. This enables
469a81ce92c381007affe6bb2310bf94c9856eaae1alanv * custom views that draw complex content to report them selves as a tree of
479a81ce92c381007affe6bb2310bf94c9856eaae1alanv * virtual views, thus conveying their logical structure.
489a81ce92c381007affe6bb2310bf94c9856eaae1alanv * </p>
499a81ce92c381007affe6bb2310bf94c9856eaae1alanv */
509a81ce92c381007affe6bb2310bf94c9856eaae1alanvpublic class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat {
519a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private static final String TAG = AccessibilityEntityProvider.class.getSimpleName();
52f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    private static final int UNDEFINED = Integer.MIN_VALUE;
539a81ce92c381007affe6bb2310bf94c9856eaae1alanv
549a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final KeyboardView mKeyboardView;
559a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final InputMethodService mInputMethodService;
569a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper;
579a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final AccessibilityUtils mAccessibilityUtils;
589a81ce92c381007affe6bb2310bf94c9856eaae1alanv
599a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /** A map of integer IDs to {@link Key}s. */
609a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final SparseArray<Key> mVirtualViewIdToKey = new SparseArray<Key>();
619a81ce92c381007affe6bb2310bf94c9856eaae1alanv
629a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /** Temporary rect used to calculate in-screen bounds. */
639a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final Rect mTempBoundsInScreen = new Rect();
649a81ce92c381007affe6bb2310bf94c9856eaae1alanv
659a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /** The parent view's cached on-screen location. */
669a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final int[] mParentLocation = new int[2];
679a81ce92c381007affe6bb2310bf94c9856eaae1alanv
68f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    /** The virtual view identifier for the focused node. */
69f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    private int mAccessibilityFocusedView = UNDEFINED;
70f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
719a81ce92c381007affe6bb2310bf94c9856eaae1alanv    public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) {
729a81ce92c381007affe6bb2310bf94c9856eaae1alanv        mKeyboardView = keyboardView;
739a81ce92c381007affe6bb2310bf94c9856eaae1alanv        mInputMethodService = inputMethod;
749a81ce92c381007affe6bb2310bf94c9856eaae1alanv
759a81ce92c381007affe6bb2310bf94c9856eaae1alanv        mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance();
769a81ce92c381007affe6bb2310bf94c9856eaae1alanv        mAccessibilityUtils = AccessibilityUtils.getInstance();
779a81ce92c381007affe6bb2310bf94c9856eaae1alanv
789a81ce92c381007affe6bb2310bf94c9856eaae1alanv        assignVirtualViewIds();
799a81ce92c381007affe6bb2310bf94c9856eaae1alanv        updateParentLocation();
809a81ce92c381007affe6bb2310bf94c9856eaae1alanv
819a81ce92c381007affe6bb2310bf94c9856eaae1alanv        // Ensure that the on-screen bounds are cleared when the layout changes.
829a81ce92c381007affe6bb2310bf94c9856eaae1alanv        mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
839a81ce92c381007affe6bb2310bf94c9856eaae1alanv    }
849a81ce92c381007affe6bb2310bf94c9856eaae1alanv
859a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /**
869a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * Creates and populates an {@link AccessibilityEvent} for the specified key
879a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * and event type.
889a81ce92c381007affe6bb2310bf94c9856eaae1alanv     *
899a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @param key A key on the host keyboard view.
909a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @param eventType The event type to create.
919a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @return A populated {@link AccessibilityEvent} for the key.
929a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @see AccessibilityEvent
939a81ce92c381007affe6bb2310bf94c9856eaae1alanv     */
949a81ce92c381007affe6bb2310bf94c9856eaae1alanv    public AccessibilityEvent createAccessibilityEvent(Key key, int eventType) {
959a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final int virtualViewId = generateVirtualViewIdForKey(key);
969a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final String keyDescription = getKeyDescription(key);
979a81ce92c381007affe6bb2310bf94c9856eaae1alanv
989a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
999a81ce92c381007affe6bb2310bf94c9856eaae1alanv        event.setPackageName(mKeyboardView.getContext().getPackageName());
1009a81ce92c381007affe6bb2310bf94c9856eaae1alanv        event.setClassName(key.getClass().getName());
1016662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv        event.setContentDescription(keyDescription);
10226c80a1b9a103cdccbaeafac75a3db2543a9ee7ealanv        event.setEnabled(true);
1039a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1049a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event);
1059a81ce92c381007affe6bb2310bf94c9856eaae1alanv        record.setSource(mKeyboardView, virtualViewId);
1069a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1079a81ce92c381007affe6bb2310bf94c9856eaae1alanv        return event;
1089a81ce92c381007affe6bb2310bf94c9856eaae1alanv    }
1099a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1109a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /**
1119a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual
1129a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or
1139a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
1149a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * <p>
1159a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * A virtual descendant is an imaginary View that is reported as a part of
1169a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * the view hierarchy for accessibility purposes. This enables custom views
1179a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * that draw complex content to report them selves as a tree of virtual
1189a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * views, thus conveying their logical structure.
1199a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * </p>
1209a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * <p>
1219a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * The implementer is responsible for obtaining an accessibility node info
1229a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * from the pool of reusable instances and setting the desired properties of
1239a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * the node info before returning it.
1249a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * </p>
1259a81ce92c381007affe6bb2310bf94c9856eaae1alanv     *
1269a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @param virtualViewId A client defined virtual view id.
1279a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual
1289a81ce92c381007affe6bb2310bf94c9856eaae1alanv     *         descendant or the host View.
1299a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @see AccessibilityNodeInfoCompat
1309a81ce92c381007affe6bb2310bf94c9856eaae1alanv     */
1319a81ce92c381007affe6bb2310bf94c9856eaae1alanv    @Override
1329a81ce92c381007affe6bb2310bf94c9856eaae1alanv    public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
1339a81ce92c381007affe6bb2310bf94c9856eaae1alanv        AccessibilityNodeInfoCompat info = null;
1349a81ce92c381007affe6bb2310bf94c9856eaae1alanv
135f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        if (virtualViewId == UNDEFINED) {
136f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            return null;
137f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        } else  if (virtualViewId == View.NO_ID) {
1389a81ce92c381007affe6bb2310bf94c9856eaae1alanv            // We are requested to create an AccessibilityNodeInfo describing
1399a81ce92c381007affe6bb2310bf94c9856eaae1alanv            // this View, i.e. the root of the virtual sub-tree.
1409a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info = AccessibilityNodeInfoCompat.obtain(mKeyboardView);
1419a81ce92c381007affe6bb2310bf94c9856eaae1alanv            ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, info);
1429a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1439a81ce92c381007affe6bb2310bf94c9856eaae1alanv            // Add the virtual children of the root View.
1449a81ce92c381007affe6bb2310bf94c9856eaae1alanv            final Keyboard keyboard = mKeyboardView.getKeyboard();
145b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka            final Key[] keys = keyboard.mKeys;
1469a81ce92c381007affe6bb2310bf94c9856eaae1alanv            for (Key key : keys) {
1479a81ce92c381007affe6bb2310bf94c9856eaae1alanv                final int childVirtualViewId = generateVirtualViewIdForKey(key);
1489a81ce92c381007affe6bb2310bf94c9856eaae1alanv                info.addChild(mKeyboardView, childVirtualViewId);
1499a81ce92c381007affe6bb2310bf94c9856eaae1alanv            }
1509a81ce92c381007affe6bb2310bf94c9856eaae1alanv        } else {
1519a81ce92c381007affe6bb2310bf94c9856eaae1alanv            // Find the view that corresponds to the given id.
1529a81ce92c381007affe6bb2310bf94c9856eaae1alanv            final Key key = mVirtualViewIdToKey.get(virtualViewId);
1539a81ce92c381007affe6bb2310bf94c9856eaae1alanv            if (key == null) {
1549a81ce92c381007affe6bb2310bf94c9856eaae1alanv                Log.e(TAG, "Invalid virtual view ID: " + virtualViewId);
1559a81ce92c381007affe6bb2310bf94c9856eaae1alanv                return null;
1569a81ce92c381007affe6bb2310bf94c9856eaae1alanv            }
1579a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1589a81ce92c381007affe6bb2310bf94c9856eaae1alanv            final String keyDescription = getKeyDescription(key);
1599a81ce92c381007affe6bb2310bf94c9856eaae1alanv            final Rect boundsInParent = key.mHitBox;
1609a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1619a81ce92c381007affe6bb2310bf94c9856eaae1alanv            // Calculate the key's in-screen bounds.
1629a81ce92c381007affe6bb2310bf94c9856eaae1alanv            mTempBoundsInScreen.set(boundsInParent);
1639a81ce92c381007affe6bb2310bf94c9856eaae1alanv            mTempBoundsInScreen.offset(mParentLocation[0], mParentLocation[1]);
1649a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1659a81ce92c381007affe6bb2310bf94c9856eaae1alanv            final Rect boundsInScreen = mTempBoundsInScreen;
1669a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1679a81ce92c381007affe6bb2310bf94c9856eaae1alanv            // Obtain and initialize an AccessibilityNodeInfo with
1689a81ce92c381007affe6bb2310bf94c9856eaae1alanv            // information about the virtual view.
1699a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info = AccessibilityNodeInfoCompat.obtain();
1709a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info.setPackageName(mKeyboardView.getContext().getPackageName());
1719a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info.setClassName(key.getClass().getName());
1726662e2a40dc764d5b6a55c0e30ce650fd834afb6alanv            info.setContentDescription(keyDescription);
1739a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info.setBoundsInParent(boundsInParent);
1749a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info.setBoundsInScreen(boundsInScreen);
1759a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info.setParent(mKeyboardView);
1769a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info.setSource(mKeyboardView, virtualViewId);
1779a81ce92c381007affe6bb2310bf94c9856eaae1alanv            info.setBoundsInScreen(boundsInScreen);
17826c80a1b9a103cdccbaeafac75a3db2543a9ee7ealanv            info.setEnabled(true);
179f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            info.setClickable(true);
180f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
181f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
182f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            if (mAccessibilityFocusedView == virtualViewId) {
183f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
184f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            } else {
185f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
186f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            }
1879a81ce92c381007affe6bb2310bf94c9856eaae1alanv        }
1889a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1899a81ce92c381007affe6bb2310bf94c9856eaae1alanv        return info;
1909a81ce92c381007affe6bb2310bf94c9856eaae1alanv    }
1919a81ce92c381007affe6bb2310bf94c9856eaae1alanv
1929a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /**
193f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * Simulates a key press by injecting touch events into the keyboard view.
194f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * This avoids the complexity of trackers and listeners within the keyboard.
195f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     *
196f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * @param key The key to press.
197f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     */
198f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    void simulateKeyPress(Key key) {
199f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final int x = key.mX + (key.mWidth / 2);
200f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final int y = key.mY + (key.mHeight / 2);
201f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final long downTime = SystemClock.uptimeMillis();
202f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final MotionEvent downEvent = MotionEvent.obtain(
203f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0);
204f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final MotionEvent upEvent = MotionEvent.obtain(
205f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);
206f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
207f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        mKeyboardView.onTouchEvent(downEvent);
208f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        mKeyboardView.onTouchEvent(upEvent);
209f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    }
210f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
211f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    @Override
212f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    public boolean performAction(int virtualViewId, int action, Bundle arguments) {
213f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final Key key = mVirtualViewIdToKey.get(virtualViewId);
214f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
215f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        if (key == null) {
216f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            return false;
217f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        }
218f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
219f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        return performActionForKey(key, action, arguments);
220f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    }
221f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
222f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    /**
223f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * Performs the specified accessibility action for the given key.
224f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     *
225f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * @param key The on which to perform the action.
226f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * @param action The action to perform.
227f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * @param arguments The action's arguments.
228f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * @return The result of performing the action, or false if the action is
229f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     *         not supported.
230f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     */
231f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    boolean performActionForKey(Key key, int action, Bundle arguments) {
232f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final int virtualViewId = generateVirtualViewIdForKey(key);
233f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
234f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        switch (action) {
235f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        case AccessibilityNodeInfoCompat.ACTION_CLICK:
236f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            simulateKeyPress(key);
237f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            return true;
238f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
239f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            if (mAccessibilityFocusedView == virtualViewId) {
240f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                return false;
241f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            }
242f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            mAccessibilityFocusedView = virtualViewId;
243f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            sendAccessibilityEventForKey(
244f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                    key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
245f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            return true;
246f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
247f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            if (mAccessibilityFocusedView != virtualViewId) {
248f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                return false;
249f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            }
250f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            mAccessibilityFocusedView = UNDEFINED;
251f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            sendAccessibilityEventForKey(
252f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv                    key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
253f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv            return true;
254f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        }
255f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
256f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        return false;
257f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    }
258f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
259f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    @Override
260f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    public AccessibilityNodeInfoCompat findAccessibilityFocus(int virtualViewId) {
261f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        return createAccessibilityNodeInfo(mAccessibilityFocusedView);
262f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    }
263f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
264f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    @Override
265f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    public AccessibilityNodeInfoCompat accessibilityFocusSearch(int direction, int virtualViewId) {
266f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        // Focus search is not currently supported for IMEs.
267f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        return null;
268f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    }
269f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
270f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    /**
271f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * Sends an accessibility event for the given {@link Key}.
272f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     *
273f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * @param key The key that's sending the event.
274f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     * @param eventType The type of event to send.
275f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv     */
276f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    void sendAccessibilityEventForKey(Key key, int eventType) {
277f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv        final AccessibilityEvent event = createAccessibilityEvent(key, eventType);
278067689c57d353e4e37e0457989c6c2686977df9ealanv        mAccessibilityUtils.requestSendAccessibilityEvent(event);
279f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    }
280f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv
281f2eba97cc09c86f9a84b61cccf3f233e1fb85a6calanv    /**
2829a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * Returns the context-specific description for a {@link Key}.
2839a81ce92c381007affe6bb2310bf94c9856eaae1alanv     *
2849a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @param key The key to describe.
2859a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @return The context-specific description of the key.
2869a81ce92c381007affe6bb2310bf94c9856eaae1alanv     */
2879a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private String getKeyDescription(Key key) {
2889a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo();
2899a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo);
2909a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final String keyDescription = mKeyCodeDescriptionMapper.getDescriptionForKey(
2919a81ce92c381007affe6bb2310bf94c9856eaae1alanv                mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure);
2929a81ce92c381007affe6bb2310bf94c9856eaae1alanv
2939a81ce92c381007affe6bb2310bf94c9856eaae1alanv        return keyDescription;
2949a81ce92c381007affe6bb2310bf94c9856eaae1alanv    }
2959a81ce92c381007affe6bb2310bf94c9856eaae1alanv
2969a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /**
2979a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * Assigns virtual view IDs to keyboard keys and populates the related maps.
2989a81ce92c381007affe6bb2310bf94c9856eaae1alanv     */
2999a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private void assignVirtualViewIds() {
3009a81ce92c381007affe6bb2310bf94c9856eaae1alanv        final Keyboard keyboard = mKeyboardView.getKeyboard();
3019a81ce92c381007affe6bb2310bf94c9856eaae1alanv        if (keyboard == null) {
3029a81ce92c381007affe6bb2310bf94c9856eaae1alanv            return;
3039a81ce92c381007affe6bb2310bf94c9856eaae1alanv        }
3049a81ce92c381007affe6bb2310bf94c9856eaae1alanv
3059a81ce92c381007affe6bb2310bf94c9856eaae1alanv        mVirtualViewIdToKey.clear();
3069a81ce92c381007affe6bb2310bf94c9856eaae1alanv
307b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final Key[] keys = keyboard.mKeys;
308b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        for (Key key : keys) {
3099a81ce92c381007affe6bb2310bf94c9856eaae1alanv            final int virtualViewId = generateVirtualViewIdForKey(key);
3109a81ce92c381007affe6bb2310bf94c9856eaae1alanv            mVirtualViewIdToKey.put(virtualViewId, key);
3119a81ce92c381007affe6bb2310bf94c9856eaae1alanv        }
3129a81ce92c381007affe6bb2310bf94c9856eaae1alanv    }
3139a81ce92c381007affe6bb2310bf94c9856eaae1alanv
3149a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /**
3159a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * Updates the parent's on-screen location.
3169a81ce92c381007affe6bb2310bf94c9856eaae1alanv     */
3179a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private void updateParentLocation() {
3189a81ce92c381007affe6bb2310bf94c9856eaae1alanv        mKeyboardView.getLocationOnScreen(mParentLocation);
3199a81ce92c381007affe6bb2310bf94c9856eaae1alanv    }
3209a81ce92c381007affe6bb2310bf94c9856eaae1alanv
3219a81ce92c381007affe6bb2310bf94c9856eaae1alanv    /**
322b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv     * Generates a virtual view identifier for the given key. Returned
323b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv     * identifiers are valid until the next global layout state change.
3249a81ce92c381007affe6bb2310bf94c9856eaae1alanv     *
3259a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @param key The key to identify.
3269a81ce92c381007affe6bb2310bf94c9856eaae1alanv     * @return A virtual view identifier.
3279a81ce92c381007affe6bb2310bf94c9856eaae1alanv     */
3289a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private static int generateVirtualViewIdForKey(Key key) {
329b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv        // The key x- and y-coordinates are stable between layout changes.
330b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv        // Generate an identifier by bit-shifting the x-coordinate to the
331b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv        // left-half of the integer and OR'ing with the y-coordinate.
332b4c41fa813426eaff2c20837c86e3f8aaa215b6falanv        return ((0xFFFF & key.mX) << (Integer.SIZE / 2)) | (0xFFFF & key.mY);
3339a81ce92c381007affe6bb2310bf94c9856eaae1alanv    }
3349a81ce92c381007affe6bb2310bf94c9856eaae1alanv
3359a81ce92c381007affe6bb2310bf94c9856eaae1alanv    private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
3369a81ce92c381007affe6bb2310bf94c9856eaae1alanv        @Override
3379a81ce92c381007affe6bb2310bf94c9856eaae1alanv        public void onGlobalLayout() {
3389a81ce92c381007affe6bb2310bf94c9856eaae1alanv            assignVirtualViewIds();
3399a81ce92c381007affe6bb2310bf94c9856eaae1alanv            updateParentLocation();
3409a81ce92c381007affe6bb2310bf94c9856eaae1alanv        }
3419a81ce92c381007affe6bb2310bf94c9856eaae1alanv    };
3429a81ce92c381007affe6bb2310bf94c9856eaae1alanv}
343