public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { if (DrawerLayout.CAN_HIDE_DESCENDANTS) { super.onInitializeAccessibilityNodeInfo(host, info); } else { AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info); super.onInitializeAccessibilityNodeInfo(host, superNode); info.setSource(host); ViewParent parent = ViewCompat.getParentForAccessibility(host); if (parent instanceof View) { info.setParent((View) parent); } copyNodeInfoNoChildren(info, superNode); superNode.recycle(); addChildrenForAccessibility(info, (ViewGroup) host); } info.setClassName(DrawerLayout.class.getName()); info.setFocusable(false); info.setFocused(false); info.removeAction(AccessibilityActionCompat.ACTION_FOCUS); info.removeAction(AccessibilityActionCompat.ACTION_CLEAR_FOCUS); }
/** * Returns {@code true} if the node supports the specified action. This method supports actions * introduced in API level 21 and later. However, it does not support bitmasks. * */ public static boolean supportsAction(AccessibilityNodeInfoCompat node, int action) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // New actions in >= API 21 won't appear in getActions() but in getActionList(). // On Lollipop+ devices, pre-API 21 actions will also appear in getActionList(). List<AccessibilityActionCompat> actions = node.getActionList(); int size = actions.size(); for (int i = 0; i < size; ++i) { AccessibilityActionCompat actionCompat = actions.get(i); if (actionCompat.getId() == action) { return true; } } return false; } else { // On < API 21, actions aren't guaranteed to appear in getActionsList(), so we need to // check getActions() instead. return (node.getActions() & action) == action; } }
@Test public void performActionWithArgs_actionAndArgsReceivedByNode() { String argsKey = "test"; Bundle args = new Bundle(); args.putBoolean(argsKey, true); assertTrue(args.getBoolean(argsKey)); AccessibilityNodeActionNode accessibilityNodeActionNode = new AccessibilityNodeActionNode( mNodeInfoCompat, new AccessibilityActionCompat(ACTION_ID, ACTION_NAME), args); accessibilityNodeActionNode.performAction(); AccessibilityNodeInfo info = (AccessibilityNodeInfo) mNodeInfoCompat.getInfo(); ShadowAccessibilityNodeInfo shadowInfo = (ShadowAccessibilityNodeInfo) ShadowExtractor.extract(info); List<Pair<Integer, Bundle>> performedActions = shadowInfo.getPerformedActionsWithArgs(); assertEquals(1, performedActions.size()); assertEquals(ACTION_ID, performedActions.get(0).first.intValue()); assertNotNull(performedActions.get(0).second); assertTrue(performedActions.get(0).second.getBoolean(argsKey)); accessibilityNodeActionNode.recycle(); }
@Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { if (CAN_HIDE_DESCENDANTS) { super.onInitializeAccessibilityNodeInfo(host, info); } else { // Obtain a node for the host, then manually generate the list // of children to only include non-obscured views. final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info); super.onInitializeAccessibilityNodeInfo(host, superNode); info.setSource(host); final ViewParent parent = ViewCompat.getParentForAccessibility(host); if (parent instanceof View) { info.setParent((View) parent); } copyNodeInfoNoChildren(info, superNode); superNode.recycle(); addChildrenForAccessibility(info, (ViewGroup) host); } info.setClassName(DrawerLayout.class.getName()); // This view reports itself as focusable so that it can intercept // the back button, but we should prevent this view from reporting // itself as focusable to accessibility services. info.setFocusable(false); info.setFocused(false); info.removeAction(AccessibilityActionCompat.ACTION_FOCUS); info.removeAction(AccessibilityActionCompat.ACTION_CLEAR_FOCUS); }
@Override protected void onPopulateNodeForVirtualView( int virtualViewId, AccessibilityNodeInfoCompat node) { if (hasCloseIcon()) { node.setContentDescription( getContext().getString(R.string.mtrl_chip_close_icon_content_description)); node.setBoundsInParent(getCloseIconTouchBoundsInt()); node.addAction(AccessibilityActionCompat.ACTION_CLICK); node.setEnabled(isEnabled()); } else { node.setContentDescription(""); } }
public static List<AccessibilityActionCompat> getCustomActions( AccessibilityNodeInfoCompat node) { List<AccessibilityActionCompat> customActions = new ArrayList<>(); for (AccessibilityActionCompat action : node.getActionList()) { if (isCustomAction(action)) { // We don't use custom actions that doesn't have a label if (!TextUtils.isEmpty(action.getLabel())) { customActions.add(action); } } } return customActions; }
/** * Populates a menu with the context menu items for a node, searching up its ancestor * hierarchy if the current node has no custom actions. */ private void recursivelyPopulateMenuItemsForNode(TalkBackService service, ContextMenuItemBuilder menuItemBuilder, AccessibilityNodeInfoCompat node, List<ContextMenuItem> menu) { if (node == null) { return; } for (AccessibilityActionCompat action : AccessibilityNodeInfoUtils.getCustomActions(node)) { CharSequence label = action.getLabel(); int id = action.getId(); if (TextUtils.isEmpty(label)) { continue; } ContextMenuItem item = menuItemBuilder.createMenuItem(service, Menu.NONE, id, Menu.NONE, label); item.setOnMenuItemClickListener(new CustomMenuItem(id, AccessibilityNodeInfoCompat.obtain(node))); item.setCheckable(false); menu.add(item); } if (menu.isEmpty()) { recursivelyPopulateMenuItemsForNode(service, menuItemBuilder, node.getParent(), menu); } }
@Before public void setUp() { ShadowAccessibilityNodeInfo.resetObtainedInstances(); mNodeInfoCompat = new SwitchAccessNodeCompat(AccessibilityNodeInfo.obtain()); ShadowAccessibilityNodeInfo shadowInfo = (ShadowAccessibilityNodeInfo) ShadowExtractor.extract(mNodeInfoCompat.getInfo()); shadowInfo.setBoundsInScreen(NODE_BOUNDS); mAccessibilityNodeActionNode = new AccessibilityNodeActionNode( mNodeInfoCompat, new AccessibilityActionCompat(ACTION_ID, ACTION_NAME)); }
@Test public void getActionLabel_withDisambiguationNumber_shouldReturnChangedName() { AccessibilityNodeActionNode accessibilityNodeActionNode = new AccessibilityNodeActionNode( mNodeInfoCompat, new AccessibilityActionCompat(ACTION_ID, ACTION_NAME)); accessibilityNodeActionNode.setNumberToAppendToDuplicateAction(5); CharSequence actionLabel = accessibilityNodeActionNode.getActionLabel(mContext); assertFalse(TextUtils.equals(ACTION_NAME, actionLabel)); assertTrue(TextUtils.indexOf(actionLabel, ACTION_NAME) != -1); accessibilityNodeActionNode.recycle(); }
@Override protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfoCompat node) { final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect); if (!hasBounds) { // The day is invalid, kill the node. mTempRect.setEmpty(); node.setContentDescription(""); node.setBoundsInParent(mTempRect); node.setVisibleToUser(false); return; } node.setText(getDayText(virtualViewId)); node.setContentDescription(getDayDescription(virtualViewId)); node.setBoundsInParent(mTempRect); final boolean isDayEnabled = isDayEnabled(virtualViewId); if (isDayEnabled) { node.addAction(AccessibilityActionCompat.ACTION_CLICK); } node.setEnabled(isDayEnabled); if (virtualViewId == mActivatedDay) { // TODO: This should use activated once that's supported. node.setChecked(true); } }
public final void onInitializeAccessibilityNodeInfo(View paramView, AccessibilityNodeInfoCompat paramAccessibilityNodeInfoCompat) { if (DrawerLayout.CAN_HIDE_DESCENDANTS) { super.onInitializeAccessibilityNodeInfo(paramView, paramAccessibilityNodeInfoCompat); } for (;;) { paramAccessibilityNodeInfoCompat.setClassName(DrawerLayout.class.getName()); paramAccessibilityNodeInfoCompat.setFocusable(false); paramAccessibilityNodeInfoCompat.setFocused(false); paramAccessibilityNodeInfoCompat.removeAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_FOCUS); paramAccessibilityNodeInfoCompat.removeAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLEAR_FOCUS); return; AccessibilityNodeInfoCompat localAccessibilityNodeInfoCompat = AccessibilityNodeInfoCompat.obtain(paramAccessibilityNodeInfoCompat); super.onInitializeAccessibilityNodeInfo(paramView, localAccessibilityNodeInfoCompat); AccessibilityNodeInfoCompat.IMPL.setSource(paramAccessibilityNodeInfoCompat.mInfo, paramView); ViewParent localViewParent = ViewCompat.getParentForAccessibility(paramView); if ((localViewParent instanceof View)) { paramAccessibilityNodeInfoCompat.setParent((View)localViewParent); } Rect localRect = this.mTmpRect; localAccessibilityNodeInfoCompat.getBoundsInParent(localRect); AccessibilityNodeInfoCompat.IMPL.setBoundsInParent(paramAccessibilityNodeInfoCompat.mInfo, localRect); localAccessibilityNodeInfoCompat.getBoundsInScreen(localRect); AccessibilityNodeInfoCompat.IMPL.setBoundsInScreen(paramAccessibilityNodeInfoCompat.mInfo, localRect); boolean bool1 = AccessibilityNodeInfoCompat.IMPL.isVisibleToUser(localAccessibilityNodeInfoCompat.mInfo); AccessibilityNodeInfoCompat.IMPL.setVisibleToUser(paramAccessibilityNodeInfoCompat.mInfo, bool1); CharSequence localCharSequence1 = localAccessibilityNodeInfoCompat.getPackageName(); AccessibilityNodeInfoCompat.IMPL.setPackageName(paramAccessibilityNodeInfoCompat.mInfo, localCharSequence1); paramAccessibilityNodeInfoCompat.setClassName(localAccessibilityNodeInfoCompat.getClassName()); CharSequence localCharSequence2 = localAccessibilityNodeInfoCompat.getContentDescription(); AccessibilityNodeInfoCompat.IMPL.setContentDescription(paramAccessibilityNodeInfoCompat.mInfo, localCharSequence2); boolean bool2 = localAccessibilityNodeInfoCompat.isEnabled(); AccessibilityNodeInfoCompat.IMPL.setEnabled(paramAccessibilityNodeInfoCompat.mInfo, bool2); boolean bool3 = localAccessibilityNodeInfoCompat.isClickable(); AccessibilityNodeInfoCompat.IMPL.setClickable(paramAccessibilityNodeInfoCompat.mInfo, bool3); paramAccessibilityNodeInfoCompat.setFocusable(localAccessibilityNodeInfoCompat.isFocusable()); paramAccessibilityNodeInfoCompat.setFocused(localAccessibilityNodeInfoCompat.isFocused()); boolean bool4 = AccessibilityNodeInfoCompat.IMPL.isAccessibilityFocused(localAccessibilityNodeInfoCompat.mInfo); AccessibilityNodeInfoCompat.IMPL.setAccessibilityFocused(paramAccessibilityNodeInfoCompat.mInfo, bool4); boolean bool5 = localAccessibilityNodeInfoCompat.isSelected(); AccessibilityNodeInfoCompat.IMPL.setSelected(paramAccessibilityNodeInfoCompat.mInfo, bool5); boolean bool6 = localAccessibilityNodeInfoCompat.isLongClickable(); AccessibilityNodeInfoCompat.IMPL.setLongClickable(paramAccessibilityNodeInfoCompat.mInfo, bool6); paramAccessibilityNodeInfoCompat.addAction(localAccessibilityNodeInfoCompat.getActions()); AccessibilityNodeInfoCompat.IMPL.recycle(localAccessibilityNodeInfoCompat.mInfo); ViewGroup localViewGroup = (ViewGroup)paramView; int i = localViewGroup.getChildCount(); for (int j = 0; j < i; j++) { View localView = localViewGroup.getChildAt(j); if (DrawerLayout.access$400(localView)) { AccessibilityNodeInfoCompat.IMPL.addChild(paramAccessibilityNodeInfoCompat.mInfo, localView); } } } }
public static boolean isCustomAction(AccessibilityActionCompat action) { return action.getId() > SYSTEM_ACTION_MAX; }
@Override public boolean accept(TalkBackService service, AccessibilityNodeInfoCompat node) { List<AccessibilityActionCompat> actions = node.getActionList(); return actions != null && !actions.isEmpty(); }
public ClickActionDelegate(Context context, int resId) { mClickAction = new AccessibilityActionCompat( AccessibilityNodeInfoCompat.ACTION_CLICK, context.getString(resId)); }