Added virtual view hierarchy for keyboard accessibility.
Bug: 5829051 Change-Id: Ied1b6267eec616bd3b9337f6e761b0c740aa0eb2main
parent
5a0661eae8
commit
9a81ce92c3
|
@ -30,6 +30,7 @@ LOCAL_REQUIRED_MODULES := libjni_latinime
|
||||||
|
|
||||||
LOCAL_STATIC_JAVA_LIBRARIES := android-common
|
LOCAL_STATIC_JAVA_LIBRARIES := android-common
|
||||||
LOCAL_STATIC_JAVA_LIBRARIES += inputmethod-common
|
LOCAL_STATIC_JAVA_LIBRARIES += inputmethod-common
|
||||||
|
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
|
||||||
|
|
||||||
# Do not compress dictionary files to mmap dict data runtime
|
# Do not compress dictionary files to mmap dict data runtime
|
||||||
LOCAL_AAPT_FLAGS := -0 .dict
|
LOCAL_AAPT_FLAGS := -0 .dict
|
||||||
|
|
|
@ -0,0 +1,377 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.accessibility;
|
||||||
|
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.inputmethodservice.InputMethodService;
|
||||||
|
import android.support.v4.view.ViewCompat;
|
||||||
|
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||||
|
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
|
||||||
|
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||||
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.Key;
|
||||||
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
import com.android.inputmethod.keyboard.KeyboardView;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes a virtual view sub-tree for {@link KeyboardView} and generates
|
||||||
|
* {@link AccessibilityEvent}s for individual {@link Key}s.
|
||||||
|
* <p>
|
||||||
|
* A virtual sub-tree is composed of imaginary {@link View}s that are reported
|
||||||
|
* as a part of the view hierarchy for accessibility purposes. This enables
|
||||||
|
* custom views that draw complex content to report them selves as a tree of
|
||||||
|
* virtual views, thus conveying their logical structure.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat {
|
||||||
|
private static final String TAG = AccessibilityEntityProvider.class.getSimpleName();
|
||||||
|
|
||||||
|
private final KeyboardView mKeyboardView;
|
||||||
|
private final InputMethodService mInputMethodService;
|
||||||
|
private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper;
|
||||||
|
private final AccessibilityUtils mAccessibilityUtils;
|
||||||
|
|
||||||
|
/** A map of integer IDs to {@link Key}s. */
|
||||||
|
private final SparseArray<Key> mVirtualViewIdToKey = new SparseArray<Key>();
|
||||||
|
|
||||||
|
/** Temporary rect used to calculate in-screen bounds. */
|
||||||
|
private final Rect mTempBoundsInScreen = new Rect();
|
||||||
|
|
||||||
|
/** The parent view's cached on-screen location. */
|
||||||
|
private final int[] mParentLocation = new int[2];
|
||||||
|
|
||||||
|
public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) {
|
||||||
|
mKeyboardView = keyboardView;
|
||||||
|
mInputMethodService = inputMethod;
|
||||||
|
|
||||||
|
mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance();
|
||||||
|
mAccessibilityUtils = AccessibilityUtils.getInstance();
|
||||||
|
|
||||||
|
assignVirtualViewIds();
|
||||||
|
updateParentLocation();
|
||||||
|
|
||||||
|
// Ensure that the on-screen bounds are cleared when the layout changes.
|
||||||
|
mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and populates an {@link AccessibilityEvent} for the specified key
|
||||||
|
* and event type.
|
||||||
|
*
|
||||||
|
* @param key A key on the host keyboard view.
|
||||||
|
* @param eventType The event type to create.
|
||||||
|
* @return A populated {@link AccessibilityEvent} for the key.
|
||||||
|
* @see AccessibilityEvent
|
||||||
|
*/
|
||||||
|
public AccessibilityEvent createAccessibilityEvent(Key key, int eventType) {
|
||||||
|
final int virtualViewId = generateVirtualViewIdForKey(key);
|
||||||
|
final String keyDescription = getKeyDescription(key);
|
||||||
|
|
||||||
|
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
|
||||||
|
event.setPackageName(mKeyboardView.getContext().getPackageName());
|
||||||
|
event.setClassName(key.getClass().getName());
|
||||||
|
event.getText().add(keyDescription);
|
||||||
|
|
||||||
|
final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event);
|
||||||
|
record.setSource(mKeyboardView, virtualViewId);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link AccessibilityNodeInfoCompat} representing a virtual
|
||||||
|
* view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or
|
||||||
|
* the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
|
||||||
|
* <p>
|
||||||
|
* A virtual descendant is an imaginary View that is reported as a part of
|
||||||
|
* the view hierarchy for accessibility purposes. This enables custom views
|
||||||
|
* that draw complex content to report them selves as a tree of virtual
|
||||||
|
* views, thus conveying their logical structure.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The implementer is responsible for obtaining an accessibility node info
|
||||||
|
* from the pool of reusable instances and setting the desired properties of
|
||||||
|
* the node info before returning it.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param virtualViewId A client defined virtual view id.
|
||||||
|
* @return A populated {@link AccessibilityNodeInfoCompat} for a virtual
|
||||||
|
* descendant or the host View.
|
||||||
|
* @see AccessibilityNodeInfoCompat
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
|
||||||
|
AccessibilityNodeInfoCompat info = null;
|
||||||
|
|
||||||
|
if (virtualViewId == View.NO_ID) {
|
||||||
|
// We are requested to create an AccessibilityNodeInfo describing
|
||||||
|
// this View, i.e. the root of the virtual sub-tree.
|
||||||
|
info = AccessibilityNodeInfoCompat.obtain(mKeyboardView);
|
||||||
|
ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, info);
|
||||||
|
|
||||||
|
// Add the virtual children of the root View.
|
||||||
|
// TODO(alanv): Need to assign a unique ID to each key.
|
||||||
|
final Keyboard keyboard = mKeyboardView.getKeyboard();
|
||||||
|
final Set<Key> keys = keyboard.mKeys;
|
||||||
|
for (Key key : keys) {
|
||||||
|
final int childVirtualViewId = generateVirtualViewIdForKey(key);
|
||||||
|
info.addChild(mKeyboardView, childVirtualViewId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Find the view that corresponds to the given id.
|
||||||
|
final Key key = mVirtualViewIdToKey.get(virtualViewId);
|
||||||
|
if (key == null) {
|
||||||
|
Log.e(TAG, "Invalid virtual view ID: " + virtualViewId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String keyDescription = getKeyDescription(key);
|
||||||
|
final Rect boundsInParent = key.mHitBox;
|
||||||
|
|
||||||
|
// Calculate the key's in-screen bounds.
|
||||||
|
mTempBoundsInScreen.set(boundsInParent);
|
||||||
|
mTempBoundsInScreen.offset(mParentLocation[0], mParentLocation[1]);
|
||||||
|
|
||||||
|
final Rect boundsInScreen = mTempBoundsInScreen;
|
||||||
|
|
||||||
|
// Obtain and initialize an AccessibilityNodeInfo with
|
||||||
|
// information about the virtual view.
|
||||||
|
info = AccessibilityNodeInfoCompat.obtain();
|
||||||
|
info.addAction(AccessibilityNodeInfoCompat.ACTION_SELECT);
|
||||||
|
info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION);
|
||||||
|
info.setPackageName(mKeyboardView.getContext().getPackageName());
|
||||||
|
info.setClassName(key.getClass().getName());
|
||||||
|
info.setBoundsInParent(boundsInParent);
|
||||||
|
info.setBoundsInScreen(boundsInScreen);
|
||||||
|
info.setParent(mKeyboardView);
|
||||||
|
info.setSource(mKeyboardView, virtualViewId);
|
||||||
|
info.setBoundsInScreen(boundsInScreen);
|
||||||
|
info.setText(keyDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an accessibility action on a virtual view, i.e. a descendant of
|
||||||
|
* the host View, with the given <code>virtualViewId</code> or the host View itself if
|
||||||
|
* <code>virtualViewId</code> equals to {@link View#NO_ID}.
|
||||||
|
*
|
||||||
|
* @param action The action to perform.
|
||||||
|
* @param virtualViewId A client defined virtual view id.
|
||||||
|
* @return True if the action was performed.
|
||||||
|
* @see #createAccessibilityNodeInfo(int)
|
||||||
|
* @see AccessibilityNodeInfoCompat
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean performAccessibilityAction(int action, int virtualViewId) {
|
||||||
|
if (virtualViewId == View.NO_ID) {
|
||||||
|
// Perform the action on the host View.
|
||||||
|
switch (action) {
|
||||||
|
case AccessibilityNodeInfoCompat.ACTION_SELECT:
|
||||||
|
if (!mKeyboardView.isSelected()) {
|
||||||
|
mKeyboardView.setSelected(true);
|
||||||
|
return mKeyboardView.isSelected();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION:
|
||||||
|
if (mKeyboardView.isSelected()) {
|
||||||
|
mKeyboardView.setSelected(false);
|
||||||
|
return !mKeyboardView.isSelected();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Find the view that corresponds to the given id.
|
||||||
|
final Key child = mVirtualViewIdToKey.get(virtualViewId);
|
||||||
|
if (child == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Perform the action on a virtual view.
|
||||||
|
switch (action) {
|
||||||
|
case AccessibilityNodeInfoCompat.ACTION_SELECT:
|
||||||
|
// TODO: Provide some focus indicator.
|
||||||
|
return true;
|
||||||
|
case AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION:
|
||||||
|
// TODO: Provide some clear focus indicator.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds {@link AccessibilityNodeInfoCompat}s by text. The match is case
|
||||||
|
* insensitive containment. The search is relative to the virtual view, i.e.
|
||||||
|
* a descendant of the host View, with the given <code>virtualViewId</code> or the host
|
||||||
|
* View itself <code>virtualViewId</code> equals to {@link View#NO_ID}.
|
||||||
|
*
|
||||||
|
* @param virtualViewId A client defined virtual view id which defined the
|
||||||
|
* root of the tree in which to perform the search.
|
||||||
|
* @param text The searched text.
|
||||||
|
* @return A list of node info.
|
||||||
|
* @see #createAccessibilityNodeInfo(int)
|
||||||
|
* @see AccessibilityNodeInfoCompat
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(
|
||||||
|
String text, int virtualViewId) {
|
||||||
|
final String searchedLowerCase = text.toLowerCase();
|
||||||
|
final Keyboard keyboard = mKeyboardView.getKeyboard();
|
||||||
|
|
||||||
|
List<AccessibilityNodeInfoCompat> results = null;
|
||||||
|
|
||||||
|
if (virtualViewId == View.NO_ID) {
|
||||||
|
for (Key key : keyboard.mKeys) {
|
||||||
|
results = findByTextAndPopulate(searchedLowerCase, key, results);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final Key key = mVirtualViewIdToKey.get(virtualViewId);
|
||||||
|
|
||||||
|
results = findByTextAndPopulate(searchedLowerCase, key, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for {@link #findAccessibilityNodeInfosByText(String, int)}.
|
||||||
|
* Takes a current set of results and matches a specified key against a
|
||||||
|
* lower-case search string. Returns an updated list of results.
|
||||||
|
*
|
||||||
|
* @param searchedLowerCase The lower-case search string.
|
||||||
|
* @param key The key to compare against.
|
||||||
|
* @param results The current list of results, or {@code null} if no results
|
||||||
|
* found.
|
||||||
|
* @return An updated list of results, or {@code null} if no results found.
|
||||||
|
*/
|
||||||
|
private List<AccessibilityNodeInfoCompat> findByTextAndPopulate(String searchedLowerCase,
|
||||||
|
Key key, List<AccessibilityNodeInfoCompat> results) {
|
||||||
|
if (!keyContainsText(key, searchedLowerCase)) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int childVirtualViewId = generateVirtualViewIdForKey(key);
|
||||||
|
final AccessibilityNodeInfoCompat nodeInfo = createAccessibilityNodeInfo(
|
||||||
|
childVirtualViewId);
|
||||||
|
|
||||||
|
if (results == null) {
|
||||||
|
results = new LinkedList<AccessibilityNodeInfoCompat>();
|
||||||
|
}
|
||||||
|
|
||||||
|
results.add(nodeInfo);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a key's current description contains the lower-case
|
||||||
|
* search text.
|
||||||
|
*
|
||||||
|
* @param key The key to compare against.
|
||||||
|
* @param textLowerCase The lower-case search string.
|
||||||
|
* @return {@code true} if the key contains the search text.
|
||||||
|
*/
|
||||||
|
private boolean keyContainsText(Key key, String textLowerCase) {
|
||||||
|
if (key == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String description = getKeyDescription(key);
|
||||||
|
|
||||||
|
if (description == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return description.toLowerCase().contains(textLowerCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the context-specific description for a {@link Key}.
|
||||||
|
*
|
||||||
|
* @param key The key to describe.
|
||||||
|
* @return The context-specific description of the key.
|
||||||
|
*/
|
||||||
|
private String getKeyDescription(Key key) {
|
||||||
|
final EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo();
|
||||||
|
final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo);
|
||||||
|
final String keyDescription = mKeyCodeDescriptionMapper.getDescriptionForKey(
|
||||||
|
mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure);
|
||||||
|
|
||||||
|
return keyDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns virtual view IDs to keyboard keys and populates the related maps.
|
||||||
|
*/
|
||||||
|
private void assignVirtualViewIds() {
|
||||||
|
final Keyboard keyboard = mKeyboardView.getKeyboard();
|
||||||
|
if (keyboard == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVirtualViewIdToKey.clear();
|
||||||
|
|
||||||
|
final Set<Key> keySet = keyboard.mKeys;
|
||||||
|
for (Key key : keySet) {
|
||||||
|
final int virtualViewId = generateVirtualViewIdForKey(key);
|
||||||
|
mVirtualViewIdToKey.put(virtualViewId, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the parent's on-screen location.
|
||||||
|
*/
|
||||||
|
private void updateParentLocation() {
|
||||||
|
mKeyboardView.getLocationOnScreen(mParentLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a virtual view identifier for the specified key.
|
||||||
|
*
|
||||||
|
* @param key The key to identify.
|
||||||
|
* @return A virtual view identifier.
|
||||||
|
*/
|
||||||
|
private static int generateVirtualViewIdForKey(Key key) {
|
||||||
|
// The key code is unique within an instance of a Keyboard.
|
||||||
|
return key.mCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
assignVirtualViewIds();
|
||||||
|
updateParentLocation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -21,13 +21,14 @@ import android.inputmethodservice.InputMethodService;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.support.v4.view.MotionEventCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
|
||||||
import com.android.inputmethod.compat.AccessibilityManagerCompatWrapper;
|
import com.android.inputmethod.compat.AccessibilityManagerCompatUtils;
|
||||||
import com.android.inputmethod.compat.AudioManagerCompatWrapper;
|
import com.android.inputmethod.compat.AudioManagerCompatWrapper;
|
||||||
import com.android.inputmethod.compat.InputTypeCompatUtils;
|
import com.android.inputmethod.compat.InputTypeCompatUtils;
|
||||||
import com.android.inputmethod.compat.MotionEventCompatUtils;
|
import com.android.inputmethod.compat.MotionEventCompatUtils;
|
||||||
|
@ -44,7 +45,6 @@ public class AccessibilityUtils {
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private AccessibilityManager mAccessibilityManager;
|
private AccessibilityManager mAccessibilityManager;
|
||||||
private AccessibilityManagerCompatWrapper mCompatManager;
|
|
||||||
private AudioManagerCompatWrapper mAudioManager;
|
private AudioManagerCompatWrapper mAudioManager;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -77,7 +77,6 @@ public class AccessibilityUtils {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mAccessibilityManager = (AccessibilityManager) context
|
mAccessibilityManager = (AccessibilityManager) context
|
||||||
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||||
mCompatManager = new AccessibilityManagerCompatWrapper(mAccessibilityManager);
|
|
||||||
|
|
||||||
final AudioManager audioManager = (AudioManager) context
|
final AudioManager audioManager = (AudioManager) context
|
||||||
.getSystemService(Context.AUDIO_SERVICE);
|
.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
@ -94,7 +93,7 @@ public class AccessibilityUtils {
|
||||||
public boolean isTouchExplorationEnabled() {
|
public boolean isTouchExplorationEnabled() {
|
||||||
return ENABLE_ACCESSIBILITY
|
return ENABLE_ACCESSIBILITY
|
||||||
&& mAccessibilityManager.isEnabled()
|
&& mAccessibilityManager.isEnabled()
|
||||||
&& mCompatManager.isTouchExplorationEnabled();
|
&& AccessibilityManagerCompatUtils.isTouchExplorationEnabled(mAccessibilityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,13 +109,13 @@ public class AccessibilityUtils {
|
||||||
|
|
||||||
return action == MotionEventCompatUtils.ACTION_HOVER_ENTER
|
return action == MotionEventCompatUtils.ACTION_HOVER_ENTER
|
||||||
|| action == MotionEventCompatUtils.ACTION_HOVER_EXIT
|
|| action == MotionEventCompatUtils.ACTION_HOVER_EXIT
|
||||||
|| action == MotionEventCompatUtils.ACTION_HOVER_MOVE;
|
|| action == MotionEventCompat.ACTION_HOVER_MOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the device should obscure typed password characters.
|
* Returns whether the device should obscure typed password characters.
|
||||||
* Typically this means speaking "dot" in place of non-control characters.
|
* Typically this means speaking "dot" in place of non-control characters.
|
||||||
*
|
*
|
||||||
* @return {@code true} if the device should obscure password characters.
|
* @return {@code true} if the device should obscure password characters.
|
||||||
*/
|
*/
|
||||||
public boolean shouldObscureInput(EditorInfo editorInfo) {
|
public boolean shouldObscureInput(EditorInfo editorInfo) {
|
||||||
|
|
|
@ -20,13 +20,16 @@ import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.inputmethodservice.InputMethodService;
|
import android.inputmethodservice.InputMethodService;
|
||||||
import android.util.Log;
|
import android.support.v4.view.AccessibilityDelegateCompat;
|
||||||
|
import android.support.v4.view.MotionEventCompat;
|
||||||
|
import android.support.v4.view.ViewCompat;
|
||||||
|
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
|
|
||||||
import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
|
|
||||||
import com.android.inputmethod.compat.MotionEventCompatUtils;
|
import com.android.inputmethod.compat.MotionEventCompatUtils;
|
||||||
|
import com.android.inputmethod.compat.ViewParentCompatUtils;
|
||||||
import com.android.inputmethod.keyboard.Key;
|
import com.android.inputmethod.keyboard.Key;
|
||||||
import com.android.inputmethod.keyboard.Keyboard;
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
import com.android.inputmethod.keyboard.KeyboardId;
|
import com.android.inputmethod.keyboard.KeyboardId;
|
||||||
|
@ -34,14 +37,14 @@ import com.android.inputmethod.keyboard.LatinKeyboardView;
|
||||||
import com.android.inputmethod.keyboard.PointerTracker;
|
import com.android.inputmethod.keyboard.PointerTracker;
|
||||||
import com.android.inputmethod.latin.R;
|
import com.android.inputmethod.latin.R;
|
||||||
|
|
||||||
public class AccessibleKeyboardViewProxy {
|
public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
|
||||||
private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName();
|
|
||||||
private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
|
private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
|
||||||
|
|
||||||
private InputMethodService mInputMethod;
|
private InputMethodService mInputMethod;
|
||||||
private FlickGestureDetector mGestureDetector;
|
private FlickGestureDetector mGestureDetector;
|
||||||
private LatinKeyboardView mView;
|
private LatinKeyboardView mView;
|
||||||
private AccessibleKeyboardActionListener mListener;
|
private AccessibleKeyboardActionListener mListener;
|
||||||
|
private AccessibilityEntityProvider mAccessibilityNodeProvider;
|
||||||
|
|
||||||
private Key mLastHoverKey = null;
|
private Key mLastHoverKey = null;
|
||||||
|
|
||||||
|
@ -54,10 +57,6 @@ public class AccessibleKeyboardViewProxy {
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setView(LatinKeyboardView view) {
|
|
||||||
sInstance.mView = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccessibleKeyboardViewProxy() {
|
private AccessibleKeyboardViewProxy() {
|
||||||
// Not publicly instantiable.
|
// Not publicly instantiable.
|
||||||
}
|
}
|
||||||
|
@ -73,34 +72,39 @@ public class AccessibleKeyboardViewProxy {
|
||||||
mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
|
mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
/**
|
||||||
if (mView == null) {
|
* Sets the view wrapped by this proxy.
|
||||||
Log.e(TAG, "No keyboard view set!");
|
*
|
||||||
return false;
|
* @param view The view to wrap.
|
||||||
|
*/
|
||||||
|
public void setView(LatinKeyboardView view) {
|
||||||
|
if (view == null) {
|
||||||
|
// Ignore null views.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (event.getEventType()) {
|
mView = view;
|
||||||
case AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER:
|
|
||||||
final Key key = mLastHoverKey;
|
|
||||||
|
|
||||||
if (key == null)
|
// Ensure that the view has an accessibility delegate.
|
||||||
break;
|
ViewCompat.setAccessibilityDelegate(view, this);
|
||||||
|
}
|
||||||
|
|
||||||
final EditorInfo info = mInputMethod.getCurrentInputEditorInfo();
|
/**
|
||||||
final boolean shouldObscure = AccessibilityUtils.getInstance().shouldObscureInput(info);
|
* Proxy method for View.getAccessibilityNodeProvider(). This method is
|
||||||
final CharSequence description = KeyCodeDescriptionMapper.getInstance()
|
* called in SDK version 15 and higher to obtain the virtual node hierarchy
|
||||||
.getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key,
|
* provider.
|
||||||
shouldObscure);
|
*
|
||||||
|
* @return The accessibility node provider for the current keyboard.
|
||||||
if (description == null)
|
*/
|
||||||
return false;
|
@Override
|
||||||
|
public AccessibilityEntityProvider getAccessibilityNodeProvider(View host) {
|
||||||
event.getText().add(description);
|
// Instantiate the provide only when requested. Since the system
|
||||||
|
// will call this method multiple times it is a good practice to
|
||||||
break;
|
// cache the provider instance.
|
||||||
|
if (mAccessibilityNodeProvider == null) {
|
||||||
|
mAccessibilityNodeProvider = new AccessibilityEntityProvider(mView, mInputMethod);
|
||||||
}
|
}
|
||||||
|
return mAccessibilityNodeProvider;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,46 +127,94 @@ public class AccessibleKeyboardViewProxy {
|
||||||
* @param event The touch exploration hover event.
|
* @param event The touch exploration hover event.
|
||||||
* @return {@code true} if the event was handled
|
* @return {@code true} if the event was handled
|
||||||
*/
|
*/
|
||||||
/*package*/ boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) {
|
/* package */boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) {
|
||||||
final int x = (int) event.getX();
|
final int x = (int) event.getX();
|
||||||
final int y = (int) event.getY();
|
final int y = (int) event.getY();
|
||||||
|
final Key key = tracker.getKeyOn(x, y);
|
||||||
|
final Key previousKey = mLastHoverKey;
|
||||||
|
|
||||||
|
mLastHoverKey = key;
|
||||||
|
|
||||||
switch (event.getAction()) {
|
switch (event.getAction()) {
|
||||||
case MotionEventCompatUtils.ACTION_HOVER_ENTER:
|
case MotionEventCompatUtils.ACTION_HOVER_ENTER:
|
||||||
case MotionEventCompatUtils.ACTION_HOVER_MOVE:
|
case MotionEventCompatUtils.ACTION_HOVER_EXIT:
|
||||||
final Key key = tracker.getKeyOn(x, y);
|
return onHoverKey(key, event);
|
||||||
|
case MotionEventCompat.ACTION_HOVER_MOVE:
|
||||||
if (key != mLastHoverKey) {
|
if (key != previousKey) {
|
||||||
fireKeyHoverEvent(mLastHoverKey, false);
|
return onTransitionKey(key, previousKey, event);
|
||||||
mLastHoverKey = key;
|
} else {
|
||||||
fireKeyHoverEvent(mLastHoverKey, true);
|
return onHoverKey(key, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireKeyHoverEvent(Key key, boolean entering) {
|
/**
|
||||||
if (mListener == null) {
|
* Simulates a transition between two {@link Key}s by sending a HOVER_EXIT
|
||||||
Log.e(TAG, "No accessible keyboard action listener set!");
|
* on the previous key, a HOVER_ENTER on the current key, and a HOVER_MOVE
|
||||||
return;
|
* on the current key.
|
||||||
|
*
|
||||||
|
* @param currentKey The currently hovered key.
|
||||||
|
* @param previousKey The previously hovered key.
|
||||||
|
* @param event The event that triggered the transition.
|
||||||
|
* @return {@code true} if the event was handled.
|
||||||
|
*/
|
||||||
|
private boolean onTransitionKey(Key currentKey, Key previousKey, MotionEvent event) {
|
||||||
|
final int savedAction = event.getAction();
|
||||||
|
|
||||||
|
event.setAction(MotionEventCompatUtils.ACTION_HOVER_EXIT);
|
||||||
|
onHoverKey(previousKey, event);
|
||||||
|
|
||||||
|
event.setAction(MotionEventCompatUtils.ACTION_HOVER_ENTER);
|
||||||
|
onHoverKey(currentKey, event);
|
||||||
|
|
||||||
|
event.setAction(MotionEventCompat.ACTION_HOVER_MOVE);
|
||||||
|
final boolean handled = onHoverKey(currentKey, event);
|
||||||
|
|
||||||
|
event.setAction(savedAction);
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a hover event on a key. If {@link Key} extended View, this would
|
||||||
|
* be analogous to calling View.onHoverEvent(MotionEvent).
|
||||||
|
*
|
||||||
|
* @param key The currently hovered key.
|
||||||
|
* @param event The hover event.
|
||||||
|
* @return {@code true} if the event was handled.
|
||||||
|
*/
|
||||||
|
private boolean onHoverKey(Key key, MotionEvent event) {
|
||||||
|
// Null keys can't receive events.
|
||||||
|
if (key == null) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mView == null) {
|
switch (event.getAction()) {
|
||||||
Log.e(TAG, "No keyboard view set!");
|
case MotionEventCompatUtils.ACTION_HOVER_ENTER:
|
||||||
return;
|
sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER);
|
||||||
|
break;
|
||||||
|
case MotionEventCompatUtils.ACTION_HOVER_EXIT:
|
||||||
|
sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == null)
|
return true;
|
||||||
return;
|
}
|
||||||
|
|
||||||
if (entering) {
|
/**
|
||||||
mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
|
* Populates and sends an {@link AccessibilityEvent} for the specified key.
|
||||||
} else {
|
*
|
||||||
mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_EXIT);
|
* @param key The key to send an event for.
|
||||||
}
|
* @param eventType The type of event to send.
|
||||||
|
*/
|
||||||
|
private void sendAccessibilityEventForKey(Key key, int eventType) {
|
||||||
|
final AccessibilityEntityProvider nodeProvider = getAccessibilityNodeProvider(null);
|
||||||
|
final AccessibilityEvent event = nodeProvider.createAccessibilityEvent(key, eventType);
|
||||||
|
|
||||||
|
// Propagates the event up the view hierarchy.
|
||||||
|
ViewParentCompatUtils.requestSendAccessibilityEvent(mView.getParent(), mView, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class KeyboardFlickGestureDetector extends FlickGestureDetector {
|
private class KeyboardFlickGestureDetector extends FlickGestureDetector {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.android.inputmethod.accessibility;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.support.v4.view.MotionEventCompat;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
|
||||||
* properties:
|
* properties:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Begins with a {@link MotionEventCompatUtils#ACTION_HOVER_ENTER} event
|
* <li>Begins with a {@link MotionEventCompatUtils#ACTION_HOVER_ENTER} event
|
||||||
* <li>Contains any number of {@link MotionEventCompatUtils#ACTION_HOVER_MOVE}
|
* <li>Contains any number of {@link MotionEventCompat#ACTION_HOVER_MOVE}
|
||||||
* events
|
* events
|
||||||
* <li>Ends with a {@link MotionEventCompatUtils#ACTION_HOVER_EXIT} event
|
* <li>Ends with a {@link MotionEventCompatUtils#ACTION_HOVER_EXIT} event
|
||||||
* <li>Maximum duration of 250 milliseconds
|
* <li>Maximum duration of 250 milliseconds
|
||||||
|
@ -128,7 +129,7 @@ public abstract class FlickGestureDetector {
|
||||||
final float distanceSquare = calculateDistanceSquare(mCachedHoverEnter, event);
|
final float distanceSquare = calculateDistanceSquare(mCachedHoverEnter, event);
|
||||||
|
|
||||||
switch (event.getAction()) {
|
switch (event.getAction()) {
|
||||||
case MotionEventCompatUtils.ACTION_HOVER_MOVE:
|
case MotionEventCompat.ACTION_HOVER_MOVE:
|
||||||
// Consume all valid move events before timeout.
|
// Consume all valid move events before timeout.
|
||||||
return true;
|
return true;
|
||||||
case MotionEventCompatUtils.ACTION_HOVER_EXIT:
|
case MotionEventCompatUtils.ACTION_HOVER_EXIT:
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.android.inputmethod.accessibility;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Key;
|
import com.android.inputmethod.keyboard.Key;
|
||||||
import com.android.inputmethod.keyboard.Keyboard;
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
@ -27,6 +28,8 @@ import com.android.inputmethod.latin.R;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class KeyCodeDescriptionMapper {
|
public class KeyCodeDescriptionMapper {
|
||||||
|
private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName();
|
||||||
|
|
||||||
// The resource ID of the string spoken for obscured keys
|
// The resource ID of the string spoken for obscured keys
|
||||||
private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot;
|
private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot;
|
||||||
|
|
||||||
|
@ -87,12 +90,12 @@ public class KeyCodeDescriptionMapper {
|
||||||
* @return a character sequence describing the action performed by pressing
|
* @return a character sequence describing the action performed by pressing
|
||||||
* the key
|
* the key
|
||||||
*/
|
*/
|
||||||
public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key,
|
public String getDescriptionForKey(Context context, Keyboard keyboard, Key key,
|
||||||
boolean shouldObscure) {
|
boolean shouldObscure) {
|
||||||
final int code = key.mCode;
|
final int code = key.mCode;
|
||||||
|
|
||||||
if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
|
if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
|
||||||
final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
|
final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
|
||||||
if (description != null)
|
if (description != null)
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +131,7 @@ public class KeyCodeDescriptionMapper {
|
||||||
* @return a character sequence describing the action performed by pressing
|
* @return a character sequence describing the action performed by pressing
|
||||||
* the key
|
* the key
|
||||||
*/
|
*/
|
||||||
private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
|
private String getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
|
||||||
final KeyboardId keyboardId = keyboard.mId;
|
final KeyboardId keyboardId = keyboard.mId;
|
||||||
final int elementId = keyboardId.mElementId;
|
final int elementId = keyboardId.mElementId;
|
||||||
final int resId;
|
final int resId;
|
||||||
|
@ -152,10 +155,7 @@ public class KeyCodeDescriptionMapper {
|
||||||
resId = R.string.spoken_description_to_numeric;
|
resId = R.string.spoken_description_to_numeric;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
resId = -1;
|
Log.e(TAG, "Missing description for keyboard element ID:" + elementId);
|
||||||
}
|
|
||||||
|
|
||||||
if (resId < 0) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ public class KeyCodeDescriptionMapper {
|
||||||
* @param keyboard The keyboard on which the key resides.
|
* @param keyboard The keyboard on which the key resides.
|
||||||
* @return A context-sensitive description of the "Shift" key.
|
* @return A context-sensitive description of the "Shift" key.
|
||||||
*/
|
*/
|
||||||
private CharSequence getDescriptionForShiftKey(Context context, Keyboard keyboard) {
|
private String getDescriptionForShiftKey(Context context, Keyboard keyboard) {
|
||||||
final KeyboardId keyboardId = keyboard.mId;
|
final KeyboardId keyboardId = keyboard.mId;
|
||||||
final int elementId = keyboardId.mElementId;
|
final int elementId = keyboardId.mElementId;
|
||||||
final int resId;
|
final int resId;
|
||||||
|
@ -212,7 +212,7 @@ public class KeyCodeDescriptionMapper {
|
||||||
* @return a character sequence describing the action performed by pressing
|
* @return a character sequence describing the action performed by pressing
|
||||||
* the key
|
* the key
|
||||||
*/
|
*/
|
||||||
private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key,
|
private String getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key,
|
||||||
boolean shouldObscure) {
|
boolean shouldObscure) {
|
||||||
final int code = key.mCode;
|
final int code = key.mCode;
|
||||||
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.inputmethod.compat;
|
|
||||||
|
|
||||||
public class AccessibilityEventCompatUtils {
|
|
||||||
public static final int TYPE_VIEW_HOVER_ENTER = 0x80;
|
|
||||||
public static final int TYPE_VIEW_HOVER_EXIT = 0x100;
|
|
||||||
}
|
|
|
@ -20,17 +20,15 @@ import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class AccessibilityManagerCompatWrapper {
|
public class AccessibilityManagerCompatUtils {
|
||||||
private static final Method METHOD_isTouchExplorationEnabled = CompatUtils.getMethod(
|
private static final Method METHOD_isTouchExplorationEnabled = CompatUtils.getMethod(
|
||||||
AccessibilityManager.class, "isTouchExplorationEnabled");
|
AccessibilityManager.class, "isTouchExplorationEnabled");
|
||||||
|
|
||||||
private final AccessibilityManager mManager;
|
private AccessibilityManagerCompatUtils() {
|
||||||
|
// This class is non-instantiable.
|
||||||
public AccessibilityManagerCompatWrapper(AccessibilityManager manager) {
|
|
||||||
mManager = manager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTouchExplorationEnabled() {
|
public static boolean isTouchExplorationEnabled(AccessibilityManager receiver) {
|
||||||
return (Boolean) CompatUtils.invoke(mManager, false, METHOD_isTouchExplorationEnabled);
|
return (Boolean) CompatUtils.invoke(receiver, false, METHOD_isTouchExplorationEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@
|
||||||
package com.android.inputmethod.compat;
|
package com.android.inputmethod.compat;
|
||||||
|
|
||||||
public class MotionEventCompatUtils {
|
public class MotionEventCompatUtils {
|
||||||
public static final int ACTION_HOVER_MOVE = 0x7;
|
// TODO(alanv): Remove after these are added to MotionEventCompat.
|
||||||
public static final int ACTION_HOVER_ENTER = 0x9;
|
public static final int ACTION_HOVER_ENTER = 0x9;
|
||||||
public static final int ACTION_HOVER_EXIT = 0xA;
|
public static final int ACTION_HOVER_EXIT = 0xA;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.compat;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewParent;
|
||||||
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class ViewParentCompatUtils {
|
||||||
|
private static final Method METHOD_requestSendAccessibilityEvent = CompatUtils.getMethod(
|
||||||
|
ViewParent.class, "requestSendAccessibilityEvent", View.class,
|
||||||
|
AccessibilityEvent.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by a child to request from its parent to send an {@link AccessibilityEvent}.
|
||||||
|
* The child has already populated a record for itself in the event and is delegating
|
||||||
|
* to its parent to send the event. The parent can optionally add a record for itself.
|
||||||
|
* <p>
|
||||||
|
* Note: An accessibility event is fired by an individual view which populates the
|
||||||
|
* event with a record for its state and requests from its parent to perform
|
||||||
|
* the sending. The parent can optionally add a record for itself before
|
||||||
|
* dispatching the request to its parent. A parent can also choose not to
|
||||||
|
* respect the request for sending the event. The accessibility event is sent
|
||||||
|
* by the topmost view in the view tree.</p>
|
||||||
|
*
|
||||||
|
* @param child The child which requests sending the event.
|
||||||
|
* @param event The event to be sent.
|
||||||
|
* @return True if the event was sent.
|
||||||
|
*/
|
||||||
|
public static boolean requestSendAccessibilityEvent(
|
||||||
|
ViewParent receiver, View child, AccessibilityEvent event) {
|
||||||
|
return (Boolean) CompatUtils.invoke(
|
||||||
|
receiver, false, METHOD_requestSendAccessibilityEvent, child, event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -379,7 +379,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
|
||||||
|
|
||||||
// This always needs to be set since the accessibility state can
|
// This always needs to be set since the accessibility state can
|
||||||
// potentially change without the input view being re-created.
|
// potentially change without the input view being re-created.
|
||||||
AccessibleKeyboardViewProxy.setView(mKeyboardView);
|
AccessibleKeyboardViewProxy.getInstance().setView(mKeyboardView);
|
||||||
|
|
||||||
return mCurrentInputView;
|
return mCurrentInputView;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
|
||||||
import android.widget.PopupWindow;
|
import android.widget.PopupWindow;
|
||||||
|
|
||||||
import com.android.inputmethod.accessibility.AccessibilityUtils;
|
import com.android.inputmethod.accessibility.AccessibilityUtils;
|
||||||
|
@ -735,16 +734,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
VoiceProxy.getInstance().onAttachedToWindow();
|
VoiceProxy.getInstance().onAttachedToWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
|
||||||
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
|
|
||||||
return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent(
|
|
||||||
event) || super.dispatchPopulateAccessibilityEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.dispatchPopulateAccessibilityEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives hover events from the input framework. This method overrides
|
* Receives hover events from the input framework. This method overrides
|
||||||
* View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
|
* View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
|
||||||
|
|
Loading…
Reference in New Issue