下面列出了怎么用android.view.accessibility.AccessibilityNodeInfo的API类实例代码及写法,或者点击链接到github查看源代码。
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
final int scrollRange = getScrollRange();
if (scrollRange > 0) {
info.setScrollable(true);
if (isEnabled() && mScrollX > 0) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT);
}
if (isEnabled() && mScrollX < scrollRange) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT);
}
}
}
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
long accessibilityNodeId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
final Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
message.arg1 = flags;
final SomeArgs args = SomeArgs.obtain();
args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
args.argi3 = interactionId;
args.arg1 = callback;
args.arg2 = spec;
args.arg3 = interactiveRegion;
args.arg4 = arguments;
message.obj = args;
scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
}
private AccessibilityNodeInfo onProvideAutofillCompatModeAccessibilityNodeInfo() {
final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
final String packageName = getContext().getPackageName();
node.setPackageName(packageName);
node.setClassName(getClass().getName());
final int childrenSize = mVirtualViews.size();
for (int i = 0; i < childrenSize; i++) {
final Item item = mVirtualViews.valueAt(i);
if (DEBUG) {
Log.d(TAG, "Adding new A11Y child with id " + item.id + ": " + item);
}
node.addChild(this, item.id);
}
return node;
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo nodeInfo = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
nodeInfo = event.getSource();
if (nodeInfo != null) {
int eventType = event.getEventType();
if (eventType== AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||
eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
if (handledMap.get(event.getWindowId()) == null) {
boolean handled = iterateNodesAndHandle(nodeInfo);
if (handled) {
handledMap.put(event.getWindowId(), true);
}
}
}
}
}
}
@Override
public void handle(AccessibilityEvent accessibilityEvent) {
AccessibilityNodeInfo nodeInfo = mContextService.getWindowNode();
if (nodeInfo == null) {
if (BuildConfig.DEBUG) {
Log.v(TAG, "handle: null nodeInfo");
}
return;
}
if (BuildConfig.DEBUG) {
Log.d(TAG, "handle: ");
}
if (isVideoChatEnded(nodeInfo, accessibilityEvent)) {
if (!AppUtils.isInLockScreen()) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "handle: close screen");
}
//熄屏,等待下次命令
RootCmd.execRootCmd("sleep 0.1 && input keyevent " + KeyEvent.KEYCODE_POWER);
}
mContextService.setState(new IdleState(mContextService));
}
}
public void handleLuckyMoneyReceivePage(AccessibilityNodeInfo node) {
if (node == null)
return;
LogUtil.d("handleLuckyMoneyReceivePage");
AccessibilityNodeInfo nodeDetail = RedEnvelopeHelper.getWechatRedEnvelopeOpenDetailNode(node);
LogUtil.d("nodeDetail="+nodeDetail);
if (nodeDetail != null) {// the red envelope already opened
// 红包已经被打开
if (SettingHelper.getREAutoMode())
ActivityHelper.goHome(this);
} else {
AccessibilityNodeInfo nodeOpen = RedEnvelopeHelper.getWechatRedEnvelopeOpenNode(node);
LogUtil.d("nodeOpen="+nodeOpen);
if (nodeOpen != null) {
nodeOpen.performAction(AccessibilityNodeInfo.ACTION_CLICK);
nodeOpen.recycle();
} else {// this page is loading red envelope data, no action
}
}
}
/**
*
* @param root is the rootview of a given page.
* @param packageName denotes the related app for this given page.
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public static String getContactNameInChat(AccessibilityNodeInfo root, String packageName){
try{
if(packageName.equals(APP_PACKAGE_WHATSAPP)) {
return root.findAccessibilityNodeInfosByViewId(getContactNameInChatResourceId(packageName)).get(0).getText().toString();
}else if(packageName.equals(APP_PACKAGE_FACEBOOK_MESSENGER)){
return root.findAccessibilityNodeInfosByViewId(getContactNameInChatResourceId(packageName)).get(0).getContentDescription().toString();
}else{
return null;
}
}
catch (Exception exception){
return null;
}
}
private boolean longClickNode(AccessibilityNodeInfoCompat node) {
if (node == null) {
return false;
}
AccessibilityNodeInfoRef current =
AccessibilityNodeInfoRef.unOwned(node);
try {
do {
LogUtils.log(this, Log.VERBOSE,
"Considering to long click: %s",
current.get().getInfo());
if (current.get().performAction(
AccessibilityNodeInfo.ACTION_LONG_CLICK)) {
return true;
}
} while (current.parent());
} finally {
current.recycle();
}
LogUtils.log(this, Log.VERBOSE, "Long click action failed");
return false;
}
private static void startHello(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo == null) {
L.d("rootWindow为空");
return;
}
final List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("打招呼");
if (list == null || list.isEmpty()) {
return;
}
if (list.size() > 0) {
final AccessibilityNodeInfo info = list.get(list.size() - 1);
final AccessibilityNodeInfo parent = info.getParent();
if (parent == null) {
L.e("parent is null");
return;
}
info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
private void openHongBao(AccessibilityEvent event) {
String className = event.getClassName().toString();
//Log.i(TAG, className);
if (className.equals(QQ_CLASSNAME_WALLET) && isNeedBack) {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
isNeedBack = false;
} else if (className.equals(QQ_CLASSNAME_CHAT) || (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && windowState)) {
windowState = true;
if (!runState) {
AccessibilityNodeInfo info = event.getSource();
if (info == null) return;
getAllHongBao(info);
}
} else {
windowState = false;
}
}
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
node.setClassName(getClass().getName());
node.addAction(AccessibilityAction.ACTION_CLICK);
final int type = getTypeFromId(virtualViewId);
final int value = getValueFromId(virtualViewId);
final CharSequence description = getVirtualViewDescription(type, value);
node.setContentDescription(description);
getBoundsForVirtualView(virtualViewId, mTempRect);
node.setBoundsInParent(mTempRect);
final boolean selected = isVirtualViewSelected(type, value);
node.setSelected(selected);
final int nextId = getVirtualViewIdAfter(type, value);
if (nextId != INVALID_ID) {
node.setTraversalBefore(RadialTimePickerView.this, nextId);
}
}
private static int getFocusedWindowIndex(List<AccessibilityWindowInfo> windows, int focusType) {
if (windows == null) {
return WRONG_INDEX;
}
for (int i = 0, size = windows.size(); i < size; i++) {
AccessibilityWindowInfo window = windows.get(i);
if (window == null) {
continue;
}
if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY
&& window.isAccessibilityFocused()) {
return i;
} else if (focusType == AccessibilityNodeInfo.FOCUS_INPUT && window.isFocused()) {
return i;
}
}
return WRONG_INDEX;
}
private List<AccessibilityNodeInfo> get_leaf_nodes(AccessibilityNodeInfo root) {
List<AccessibilityNodeInfo> ret = new ArrayList<AccessibilityNodeInfo>();
Queue<AccessibilityNodeInfo> Q = new LinkedList<AccessibilityNodeInfo>();
Q.add(root);
while (!Q.isEmpty()) {
AccessibilityNodeInfo node = Q.poll();
if (node == null) {
Util.log("Processing NULL");
continue;
}
int childCnt = node.getChildCount();
if (childCnt > 0) {
for (int i = 0; i < childCnt; i++) {
AccessibilityNodeInfo child = node.getChild(i);
Q.add(child); // no need to check NULL, checked above
}
} else {
ret.add(node);
}
}
return ret;
}
/**
* performAutoInstall
* @param aAccessibilityEvent aAccessibilityEvent
* @param aClassName aClassName
*/
private void performAutoInstall(AccessibilityEvent aAccessibilityEvent, String aClassName) {
AccessibilityNodeInfo targetNode = extractNode(aAccessibilityEvent, aClassName,
this.getString(R.string.btn_accessibility_install));
Log.e("test", "target node 1 " + targetNode);
if (targetNode != null) {
performClickAction(targetNode);
return;
}
targetNode = extractNode(aAccessibilityEvent, aClassName,
this.getString(R.string.btn_accessibility_allow_once));
Log.e("test", "target node 2 " + targetNode);
if (targetNode != null) {
performClickAction(targetNode);
return;
}
targetNode = extractNode(aAccessibilityEvent, aClassName,
this.getString(R.string.btn_accessibility_next));
Log.e("test", "target node 3 " + targetNode);
if(targetNode != null) {
performClickAction(targetNode);
autoInstall(aAccessibilityEvent);
}
}
private void openNext2(String str, AccessibilityNodeInfo nodeInfo, final AccessibilityService service) {
if (nodeInfo == null) {
L.d("rootWindow为空");
return;
} else {
L.d("openNext2 " + nodeInfo.getText());
}
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(str);
if (list.isEmpty()) {
return;
} else {
CharSequence text = list.get(list.size() - 1).getText();
if (TextUtils.isEmpty(text)) {
return;
} else {
L.d("openNext2 " + text);
}
}
i++;
if (isGo) {
isGo = false;
}
}
/**
* A snapshot of all attributes is taken at construction. The attributes of a
* {@code UiAutomationElement} instance are immutable. If the underlying
* {@link AccessibilityNodeInfo} is updated, a new {@code UiAutomationElement}
* instance will be created in
*/
protected UiAutomationElement( AccessibilityNodeInfo node,
UiAutomationElement parent, int index) {
this.node = node;
this.parent = parent;
Map<Attribute, Object> attribs = new EnumMap<Attribute, Object>(Attribute.class);
put(attribs, Attribute.INDEX, index);
put(attribs, Attribute.PACKAGE, charSequenceToString(node.getPackageName()));
put(attribs, Attribute.CLASS, charSequenceToString(node.getClassName()));
put(attribs, Attribute.TEXT, charSequenceToString(node.getText()));
put(attribs, Attribute.CONTENT_DESC, charSequenceToString(node.getContentDescription()));
put(attribs, Attribute.RESOURCE_ID, charSequenceToString(node.getViewIdResourceName()));
put(attribs, Attribute.CHECKABLE, node.isCheckable());
put(attribs, Attribute.CHECKED, node.isChecked());
put(attribs, Attribute.CLICKABLE, node.isClickable());
put(attribs, Attribute.ENABLED, node.isEnabled());
put(attribs, Attribute.FOCUSABLE, node.isFocusable());
put(attribs, Attribute.FOCUSED, node.isFocused());
put(attribs, Attribute.LONG_CLICKABLE, node.isLongClickable());
put(attribs, Attribute.PASSWORD, node.isPassword());
put(attribs, Attribute.SCROLLABLE, node.isScrollable());
if (node.getTextSelectionStart() >= 0
&& node.getTextSelectionStart() != node.getTextSelectionEnd()) {
attribs.put(Attribute.SELECTION_START, node.getTextSelectionStart());
attribs.put(Attribute.SELECTION_END, node.getTextSelectionEnd());
}
put(attribs, Attribute.SELECTED, node.isSelected());
put(attribs, Attribute.BOUNDS, getBounds(node));
attributes = Collections.unmodifiableMap(attribs);
// Order matters as getVisibleBounds depends on visible
visible = node.isVisibleToUser();
visibleBounds = getVisibleBounds(node);
List<UiAutomationElement> mutableChildren = buildChildren(node);
this.children = mutableChildren == null ? null : Collections.unmodifiableList(mutableChildren);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
info.setClassName(SurfaceRenderView.class.getName());
}
}
private boolean isValidScrollEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return true; // Cannot check source validity, so assume that it's scrollable.
}
boolean valid =
source.isScrollable() || event.getMaxScrollX() != -1 || event.getMaxScrollY() != -1;
source.recycle();
return valid;
}
private TreeNode obtain(AccessibilityNodeInfo node) {
TreeNode res = mPool.acquire();
if (res == null) {
res = new TreeNode();
}
res.sealed = false;
if (Ln.isDebugEnabled()) {
dumpTreeNodePool("obtain B " + super.toString());
}
res.init(node);
return res;
}
@Override
public AbstractNodeTree loadNode(Object source, AbstractNodeTree parent, NodeContext context) {
if (source instanceof AccessibilityNodeInfo) {
try {
return AccessibilityUtil.buildCurrentNode((AccessibilityNodeInfo) source, parent);
} catch (Exception e) {
LogUtil.e(TAG, "Catch java.lang.Exception: " + e.getMessage(), e);
}
} else if (source instanceof FakeNodeTree) {
return (FakeNodeTree) source;
}
return null;
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (currentMessageObject.isMusic()) {
info.setText(LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle()));
} else if (titleLayout != null && descriptionLayout != null) {
info.setText(titleLayout.getText() + ", " + descriptionLayout.getText());
}
if (checkBox.isChecked()) {
info.setCheckable(true);
info.setChecked(true);
}
}
@Nullable
public static UiAutomationElement getCachedElement(AccessibilityNodeInfo rawElement, AccessibilityNodeInfo[] windowRoots) {
if (cache.get(rawElement) == null) {
rebuildForNewRoots(windowRoots);
}
return cache.get(rawElement);
}
/**
* 点击视频聊天按钮
* @param nodeInfo
* @return 是否成功
*/
private boolean startVideoChat(AccessibilityNodeInfo nodeInfo) {
List<AccessibilityNodeInfo> videoTextNodes = nodeInfo.findAccessibilityNodeInfosByText("视频电话");
if (!isListEmpty(videoTextNodes)) {
for (AccessibilityNodeInfo textNode : videoTextNodes) {
if (textNode.getClassName().toString().contains(RelativeLayout.class.getName())) { //contextDesc
textNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
return true;
}
}
}
return false;
}
/**
* Tries to set accessibility focus on the given node. It's used by {@link FocusActor} to set
* accessibility focus.
*
* <p>This method attempts to focus the node only when the node is not accessibility focus or when
* {@code forceRefocusIfAlreadyFocused} is {@code true}.
*
* <p><strong>Note: </strong> Caller is responsible to recycle {@code node}.
*
* @param node Node to be focused.
* @param forceRefocusIfAlreadyFocused Whether we should perform ACTION_ACCESSIBILITY_FOCUS if the
* node is already accessibility focused.
* @param eventId The EventId for performance tracking.
* @return Whether the node is already accessibility focused or we successfully put accessibility
* focus on the node.
*/
boolean setAccessibilityFocus(
AccessibilityNodeInfoCompat node,
boolean forceRefocusIfAlreadyFocused,
final FocusActionInfo focusActionInfo,
EventId eventId) {
if (isAccessibilityFocused(node)) {
if (forceRefocusIfAlreadyFocused) {
PerformActionUtils.performAction(
node, AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, eventId);
} else {
return true;
}
}
// Accessibility focus follows input focus on TVs, we want to set both simultaneously,
// so we change the input focus if possible.
// Instead of syncing a11y focus when TYPE_VIEW_FOCUSED event is received, we immediately
// perform a11y focus action after input focus action, in case that we don't receive the result
// TYPE_VIEW_FOCUSED in some weird cases.
if (controlInputFocus && node.isFocusable() && !node.isFocused()) {
boolean result =
PerformActionUtils.performAction(node, AccessibilityNodeInfo.ACTION_FOCUS, eventId);
LogUtils.d(
TAG,
"Perform input focus action:result=%s\n" + " eventId=%s," + " Node=%s",
result,
eventId,
node);
if (result) {
actorState.setInputFocus(node);
}
}
return performAccessibilityFocusActionInternal(node, focusActionInfo, eventId);
}
/**
* Performs a two-pointer gesture, where each pointer moves diagonally
* opposite across the other, from the center out towards the edges of the
* this UiObject.
* @param percent percentage of the object's diagonal length for the pinch gesture
* @param steps the number of steps for the gesture. Steps are injected
* about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
* @return <code>true</code> if all touch events for this gesture are injected successfully,
* <code>false</code> otherwise
* @throws UiObjectNotFoundException
* @since API Level 18
*/
public boolean pinchOut(int percent, int steps) throws UiObjectNotFoundException {
// make value between 1 and 100
percent = (percent < 0) ? 1 : (percent > 100) ? 100 : percent;
float percentage = percent / 100f;
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
if (node == null) {
throw new UiObjectNotFoundException(mUiSelector.toString());
}
Rect rect = getVisibleBounds(node);
if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2)
throw new IllegalStateException("Object width is too small for operation");
// start from the same point at the center of the control
Point startPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY());
Point startPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY());
// End at the top-left and bottom-right corners of the control
Point endPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage),
rect.centerY());
Point endPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage),
rect.centerY());
return performTwoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps);
}
/**
* Check if the view's <code>long-clickable</code> property is currently true
*
* @return true if it is else false
* @throws UiObjectNotFoundException
* @since API Level 16
*/
public boolean isLongClickable() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
if(node == null) {
throw new UiObjectNotFoundException(mUiSelector.toString());
}
return node.isLongClickable();
}
private UIState getCurrentUIState() {
root = getRootNode();
Hashtable<String, Integer> map = new Hashtable<String, Integer>();
// computeFeatureVector(map, root, 0);
Hashtable<String, Integer> dict = new Hashtable<String, Integer>();
computeFeatureVectorExactMatch(dict, map, root, 0);
List<AccessibilityNodeInfo> clickables = getClickables(root);
UIState state = new UIState(map, clickables);
return state;
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (currentMessageObject.isMusic()) {
info.setText(LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle()));
} else if (titleLayout != null && descriptionLayout != null) {
info.setText(titleLayout.getText() + ", " + descriptionLayout.getText());
}
if (checkBox.isChecked()) {
info.setCheckable(true);
info.setChecked(true);
}
}
private void sendWindowChangeEventsToWindowChangedListener(
WindowChangedListener windowChangedListener, List<AccessibilityEvent> windowChangeEventList) {
if (isRunning) {
while (!windowChangeEventList.isEmpty()) {
AccessibilityEvent event = windowChangeEventList.get(0);
CharSequence packageName = event.getPackageName();
if (service.getPackageName().equals(packageName)) {
AccessibilityNodeInfo info = event.getSource();
if (info != null) {
CharSequence className = info.getClassName();
// Check whether the window events in the event list are triggered by opening the
// Switch Access menu window. If so, clears the event list and calls
// WindowChangeListener#onSwitchAccessMenuShown to generate screen feedback for
// the Switch Access menu.
//
// We use two criteria to check if a window is a Switch Access menu window:
// 1. The package of the event is the same as the Switch Access Accessibility
// Service.
// 2. The source AccessibilityNodeInfoCompat of the event has the same class name
// as SwitchAccessMenuOverlay.
if (switchAccessMenus.containsKey(className)) {
windowChangedListener.onSwitchAccessMenuShown(switchAccessMenus.get(className));
windowChangeEventList.clear();
return;
}
}
}
windowChangedListener.onWindowChangedAndIsNowStable(
windowChangeEventList.get(0), Performance.EVENT_ID_UNTRACKED);
windowChangeEventList.remove(0);
}
}
}
private static void getLeaves(List<AccessibilityNodeInfo> leaves, AccessibilityNodeInfo node) {
if (node.getChildCount() == 0) {
leaves.add(node);
return;
}
for (int i = 0; i < node.getChildCount(); i++) {
getLeaves(leaves, node.getChild(i));
}
}