Java Code Examples for androidx.core.view.accessibility.AccessibilityNodeInfoCompat#obtain()

The following examples show how to use androidx.core.view.accessibility.AccessibilityNodeInfoCompat#obtain() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: MaterialButtonToggleGroupTest.java    From material-components-android with Apache License 2.0 6 votes vote down vote up
@Test
@Config(sdk = 23)
public void onInitializeAccessibilityNodeInfo() {
  AccessibilityNodeInfoCompat groupInfoCompat = AccessibilityNodeInfoCompat.obtain();
  ViewCompat.onInitializeAccessibilityNodeInfo(toggleGroup, groupInfoCompat);

  CollectionInfoCompat collectionInfo = groupInfoCompat.getCollectionInfo();
  assertEquals(3, collectionInfo.getColumnCount());
  assertEquals(1, collectionInfo.getRowCount());

  MaterialButton secondChild = (MaterialButton) toggleGroup.getChildAt(1);
  secondChild.setChecked(true);
  AccessibilityNodeInfoCompat buttonInfoCompat = AccessibilityNodeInfoCompat.obtain();
  ViewCompat.onInitializeAccessibilityNodeInfo(secondChild, buttonInfoCompat);

  CollectionItemInfoCompat itemInfo = buttonInfoCompat.getCollectionItemInfo();
  assertEquals(1, itemInfo.getColumnIndex());
  assertEquals(0, itemInfo.getRowIndex());
  assertTrue(itemInfo.isSelected());
}
 
Example 2
Source File: DirectionalTraversalStrategy.java    From talkback with Apache License 2.0 6 votes vote down vote up
public DirectionalTraversalStrategy(AccessibilityNodeInfoCompat root) {
  mRoot = AccessibilityNodeInfoCompat.obtain(root);

  mRootRect = new Rect();
  mRoot.getBoundsInScreen(mRootRect);

  int fudge = -(mRootRect.width() / 20); // 5% fudge factor to catch objects near edge.
  mRootRectPadded = new Rect(mRootRect);
  mRootRectPadded.inset(fudge, fudge);

  processNodes(mRoot, false /* forceRefresh */);

  // Before N, sometimes AccessibilityNodeInfo is not properly updated after transitions
  // occur. This was fixed in a system framework change for N. (See  for context.)
  // To work-around, manually refresh AccessibilityNodeInfo if it initially
  // looks like there's nothing to focus on.
  if (mFocusables.isEmpty() && !BuildVersionUtils.isAtLeastN()) {
    recycle(false /* recycleRoot */);
    processNodes(mRoot, true /* forceRefresh */);
  }
}
 
Example 3
Source File: DirectionalTraversalStrategy.java    From talkback with Apache License 2.0 5 votes vote down vote up
@Override
public @Nullable AccessibilityNodeInfoCompat focusInitial(
    AccessibilityNodeInfoCompat root, int direction) {
  if (root == null) {
    return null;
  }

  Rect rootRect = new Rect();
  root.getBoundsInScreen(rootRect);

  AccessibilityNodeInfoCompat focusedNode =
      root.findFocus(AccessibilityNodeInfoCompat.FOCUS_ACCESSIBILITY);

  Rect searchRect = new Rect();
  if (focusedNode != null) {
    getSearchStartRect(focusedNode, direction, searchRect);
  } else if (direction == TraversalStrategy.SEARCH_FOCUS_LEFT) {
    searchRect.set(rootRect.right, rootRect.top, rootRect.right + 1, rootRect.bottom);
  } else if (direction == TraversalStrategy.SEARCH_FOCUS_RIGHT) {
    searchRect.set(rootRect.left - 1, rootRect.top, rootRect.left, rootRect.bottom);
  } else if (direction == TraversalStrategy.SEARCH_FOCUS_UP) {
    searchRect.set(rootRect.left, rootRect.bottom, rootRect.right, rootRect.bottom + 1);
  } else {
    searchRect.set(rootRect.left, rootRect.top - 1, rootRect.right, rootRect.top);
  }

  AccessibilityNodeInfoCompat newFocus = findFocus(focusedNode, searchRect, direction);
  if (newFocus != null) {
    return AccessibilityNodeInfoCompat.obtain(newFocus);
  }

  return null;
}
 
Example 4
Source File: BottomNavigationViewTest.java    From material-components-android with Apache License 2.0 5 votes vote down vote up
@UiThreadTest
@Test
@SmallTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
public void testOnInitializeAccessibilityNodeInfo() {
  BottomNavigationMenuView menuView = bottomNavigation.menuView;

  AccessibilityNodeInfoCompat groupInfoCompat = AccessibilityNodeInfoCompat.obtain();
  ViewCompat.onInitializeAccessibilityNodeInfo(menuView, groupInfoCompat);

  CollectionInfoCompat collectionInfo = groupInfoCompat.getCollectionInfo();
  Assert.assertEquals(3, collectionInfo.getColumnCount());
  Assert.assertEquals(1, collectionInfo.getRowCount());

  BottomNavigationItemView secondChild = (BottomNavigationItemView) menuView.getChildAt(1);
  secondChild.setSelected(true);
  AccessibilityNodeInfoCompat buttonInfoCompat = AccessibilityNodeInfoCompat.obtain();
  ViewCompat.onInitializeAccessibilityNodeInfo(secondChild, buttonInfoCompat);

  // A tab that is currently selected won't be clickable by a11y
  assertFalse(buttonInfoCompat.isClickable());
  assertFalse(
      AccessibilityUtils.hasAction(buttonInfoCompat, AccessibilityActionCompat.ACTION_CLICK));

  CollectionItemInfoCompat itemInfo = buttonInfoCompat.getCollectionItemInfo();
  Assert.assertEquals(1, itemInfo.getColumnIndex());
  Assert.assertEquals(0, itemInfo.getRowIndex());
  assertTrue(itemInfo.isSelected());
}
 
Example 5
Source File: TabLayoutTest.java    From material-components-android with Apache License 2.0 5 votes vote down vote up
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
public void initializesAccessibilityNodeInfo() {
  final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
  final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);

  final TabLayout.Tab tab1 = tabs.newTab();
  tabs.addTab(tab1);
  final TabLayout.Tab tab2 = tabs.newTab();
  tabs.addTab(tab2, true);

  tabs.getTabAt(0).setCustomView(R.layout.design_tab_item_custom);
  tabs.getTabAt(1).setCustomView(R.layout.design_tab_item_custom);

  AccessibilityNodeInfoCompat groupInfoCompat = AccessibilityNodeInfoCompat.obtain();
  ViewCompat.onInitializeAccessibilityNodeInfo(tabs, groupInfoCompat);

  CollectionInfoCompat collectionInfo = groupInfoCompat.getCollectionInfo();
  assertEquals(2, collectionInfo.getColumnCount());
  assertEquals(1, collectionInfo.getRowCount());

  TabView secondChild = tabs.getTabAt(1).view;
  secondChild.setSelected(true);
  AccessibilityNodeInfoCompat tabInfoCompat = AccessibilityNodeInfoCompat.obtain();
  ViewCompat.onInitializeAccessibilityNodeInfo(secondChild, tabInfoCompat);

  // A tab that is currently selected won't be clickable
  assertFalse(tabInfoCompat.isClickable());
  assertFalse(
      AccessibilityUtils.hasAction(tabInfoCompat, AccessibilityActionCompat.ACTION_CLICK));

  CollectionItemInfoCompat itemInfo = tabInfoCompat.getCollectionItemInfo();
  assertEquals(1, itemInfo.getColumnIndex());
  assertEquals(0, itemInfo.getRowIndex());
  assertTrue(itemInfo.isSelected());
}
 
Example 6
Source File: OrderedTraversalController.java    From talkback with Apache License 2.0 5 votes vote down vote up
/** Searches last node to be focused */
public @Nullable AccessibilityNodeInfoCompat findLast() {
  if (mTree == null) {
    return null;
  }

  return AccessibilityNodeInfoCompat.obtain(mTree.getRoot().getLastNode().getNode());
}
 
Example 7
Source File: VariablesFactory.java    From talkback with Apache License 2.0 5 votes vote down vote up
VariableDelegate createLocalVariableDelegate(
    @Nullable AccessibilityEvent event,
    @Nullable AccessibilityNodeInfoCompat node,
    @Nullable EventInterpretation interpretation) {
  VariableDelegate delegate = mGlobalVariables;
  if (event != null) {
    delegate =
        new EventVariables(mContext, delegate, event, event.getSource(), mUserPreferredLocale);
  }

  if (interpretation != null) {
    delegate =
        new InterpretationVariables(mContext, delegate, interpretation, mUserPreferredLocale);
  }

  // Node variables is constructed last. This ensures that child nodes it creates have access to
  // top level global variables.
  if (node != null) {
    delegate =
        new NodeVariables(
            mContext,
            mLabelManager,
            nodeMenuProvider,
            delegate,
            AccessibilityNodeInfoCompat.obtain(node),
            mUserPreferredLocale);
  }
  return delegate;
}
 
Example 8
Source File: AccessibilityNode.java    From talkback with Apache License 2.0 5 votes vote down vote up
/** Returns a node instance, or null. Should only be called by this class and sub-classes. */
@Nullable
protected static <T extends AccessibilityNode> T construct(
    @Nullable AccessibilityNodeInfoCompat nodeArg, boolean copy, Factory<T> factory) {
  // See implementation notes in overloaded construct() method, above.
  if (nodeArg == null) {
    return null;
  }
  T instance = factory.create();
  AccessibilityNode instanceBase = instance;
  instanceBase.nodeCompat = copy ? AccessibilityNodeInfoCompat.obtain(nodeArg) : nodeArg;
  return instance;
}
 
Example 9
Source File: TraversalStrategyUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
/**
 * Search focus that satisfied specified node filter from currentFocus to specified direction
 * according to OrderTraversal strategy
 *
 * @param traversal - order traversal strategy
 * @param currentFocus - node that is starting point of focus search
 * @param direction - direction the target focus is searching to
 * @param filter - filters focused node candidate
 * @return node that could be focused next
 */
public static @Nullable AccessibilityNodeInfoCompat searchFocus(
    TraversalStrategy traversal,
    AccessibilityNodeInfoCompat currentFocus,
    @TraversalStrategy.SearchDirection int direction,
    Filter<AccessibilityNodeInfoCompat> filter) {
  if (traversal == null || currentFocus == null) {
    return null;
  }

  if (filter == null) {
    filter = DEFAULT_FILTER;
  }

  AccessibilityNodeInfoCompat targetNode = AccessibilityNodeInfoCompat.obtain(currentFocus);
  Set<AccessibilityNodeInfoCompat> seenNodes = new HashSet<>();
  try {
    do {
      seenNodes.add(targetNode);
      targetNode = traversal.findFocus(targetNode, direction);

      if (seenNodes.contains(targetNode)) {
        LogUtils.e(TAG, "Found duplicate during traversal: %s", targetNode);
        return null;
      }
    } while (targetNode != null && !filter.accept(targetNode));
  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(seenNodes);
  }

  return targetNode;
}
 
Example 10
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
/** Caller retains responsibility to recycle nodeArg, and created span. */
public ClickableSpanFromBundle(
    int originalClickableSpanIdArg,
    AccessibilityNodeInfoCompat nodeArg,
    int clickableSpanActionIdArg) {
  originalClickableSpanId = originalClickableSpanIdArg;

  // TODO: When menus can recycle their nodes, stop copying the node here, just
  // keep a copy in the menu-item created by RuleSpannables.
  node = AccessibilityNodeInfoCompat.obtain(nodeArg);

  clickableSpanActionId = clickableSpanActionIdArg;
}
 
Example 11
Source File: RuleGranularity.java    From talkback with Apache License 2.0 5 votes vote down vote up
public GranularityMenuItemClickListener(
    TalkBackService service,
    Pipeline.FeedbackReturner pipeline,
    AccessibilityNodeInfoCompat node,
    boolean hasWebContent) {
  analytics = service.getAnalytics();
  this.pipeline = pipeline;
  this.node = AccessibilityNodeInfoCompat.obtain(node);
  this.hasWebContent = hasWebContent;
}
 
Example 12
Source File: OrderedTraversalStrategy.java    From talkback with Apache License 2.0 5 votes vote down vote up
private @Nullable AccessibilityNodeInfoCompat focusPrevious(AccessibilityNodeInfoCompat node) {
  AccessibilityNodeInfoCompat rootNode = AccessibilityNodeInfoCompat.obtain(node);
  AccessibilityNodeInfoCompat targetNode;
  try {
    targetNode = mController.findPrevious(rootNode);
  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(rootNode);
  }

  return targetNode;
}
 
Example 13
Source File: OrderedTraversalController.java    From talkback with Apache License 2.0 5 votes vote down vote up
public @Nullable AccessibilityNodeInfoCompat findLast(AccessibilityNodeInfoCompat rootNode) {
  if (rootNode == null) {
    return null;
  }

  WorkingTree tree = mNodeTreeMap.get(rootNode);
  if (tree == null) {
    return null;
  }

  return AccessibilityNodeInfoCompat.obtain(tree.getLastNode().getNode());
}
 
Example 14
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
/**
 * Returns a fresh copy of {@code node} with properties that are less likely to be stale. Returns
 * {@code null} if the node can't be found anymore.
 */
public static AccessibilityNodeInfoCompat refreshNode(AccessibilityNodeInfoCompat node) {
  if (node == null) {
    return null;
  }

  AccessibilityNodeInfoCompat nodeCopy = AccessibilityNodeInfoCompat.obtain(node);
  if (nodeCopy.refresh()) {
    return nodeCopy;
  } else {
    nodeCopy.recycle();
    return null;
  }
}
 
Example 15
Source File: OrderedTraversalController.java    From talkback with Apache License 2.0 5 votes vote down vote up
public @Nullable AccessibilityNodeInfoCompat findFirst(AccessibilityNodeInfoCompat rootNode) {
  if (rootNode == null) {
    return null;
  }

  WorkingTree tree = mNodeTreeMap.get(rootNode);
  if (tree == null) {
    return null;
  }

  return AccessibilityNodeInfoCompat.obtain(tree.getNode());
}
 
Example 16
Source File: RuleUnlabeledImage.java    From talkback with Apache License 2.0 4 votes vote down vote up
@Override
public List<ContextMenuItem> getMenuItemsForNode(
    TalkBackService service,
    ContextMenuItemBuilder menuItemBuilder,
    AccessibilityNodeInfoCompat node,
    boolean includeAncestors) {
  List<ContextMenuItem> items = new ArrayList<>();
  CustomLabelManager labelManager = service.getLabelManager();
  if (labelManager == null) {
    return items;
  }

  AccessibilityNodeInfoCompat nodeCopy = AccessibilityNodeInfoCompat.obtain(node);
  Label viewLabel = labelManager.getLabelForViewIdFromCache(nodeCopy.getViewIdResourceName());
  if (viewLabel == null) {
    final ContextMenuItem addLabel =
        menuItemBuilder.createMenuItem(
            service,
            Menu.NONE,
            R.id.labeling_breakout_add_label,
            Menu.NONE,
            service.getString(R.string.label_dialog_title_add));
    items.add(addLabel);
  } else {
    ContextMenuItem editLabel =
        menuItemBuilder.createMenuItem(
            service,
            Menu.NONE,
            R.id.labeling_breakout_edit_label,
            Menu.NONE,
            service.getString(R.string.label_dialog_title_edit));
    ContextMenuItem removeLabel =
        menuItemBuilder.createMenuItem(
            service,
            Menu.NONE,
            R.id.labeling_breakout_remove_label,
            Menu.NONE,
            service.getString(R.string.label_dialog_title_remove));
    items.add(editLabel);
    items.add(removeLabel);
  }

  for (ContextMenuItem item : items) {
    item.setOnMenuItemClickListener(
        new UnlabeledImageMenuItemClickListener(service, nodeCopy, viewLabel, pipeline));
    item.setShowsAlertDialog(true);
  }

  return items;
}
 
Example 17
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Returns the first ancestor of {@code node} that matches the {@code filter}, terminating the
 * search once it reaches {@code end}. The search is exclusive of both {@code node} and {@code
 * end}. Returns {@code null} if no nodes match.
 *
 * <p><strong>Note:</strong> Caller is responsible for recycling the returned node.
 */
private static @Nullable AccessibilityNodeInfoCompat getMatchingAncestor(
    AccessibilityNodeInfoCompat node,
    AccessibilityNodeInfoCompat end,
    Filter<AccessibilityNodeInfoCompat> filter) {
  if (node == null) {
    return null;
  }

  final HashSet<AccessibilityNodeInfoCompat> ancestors = new HashSet<>();

  try {
    ancestors.add(AccessibilityNodeInfoCompat.obtain(node));
    node = node.getParent();

    while (node != null) {
      if (!ancestors.add(node)) {
        // Already seen this node, so abort!
        node.recycle();
        return null;
      }

      if (end != null && node.equals(end)) {
        // Reached the end node, so abort!
        // Don't recycle the node here, it was added to ancestors and will be recycled.
        return null;
      }

      if (filter.accept(node)) {
        // Send a copy since node gets recycled.
        return AccessibilityNodeInfoCompat.obtain(node);
      }

      node = node.getParent();
    }
  } finally {
    recycleNodes(ancestors);
  }

  return null;
}
 
Example 18
Source File: DirectionalTraversalStrategy.java    From talkback with Apache License 2.0 4 votes vote down vote up
public @Nullable AccessibilityNodeInfoCompat findFocus(
    AccessibilityNodeInfoCompat focused, Rect focusedRect, int direction) {
  // Using roughly the same algorithm as
  // frameworks/base/core/java/android/view/FocusFinder.java#findNextFocusInAbsoluteDirection

  Rect bestCandidateRect = new Rect(focusedRect);
  switch (direction) {
    case TraversalStrategy.SEARCH_FOCUS_LEFT:
      bestCandidateRect.offset(focusedRect.width() + 1, 0);
      break;
    case TraversalStrategy.SEARCH_FOCUS_RIGHT:
      bestCandidateRect.offset(-(focusedRect.width() + 1), 0);
      break;
    case TraversalStrategy.SEARCH_FOCUS_UP:
      bestCandidateRect.offset(0, focusedRect.height() + 1);
      break;
    case TraversalStrategy.SEARCH_FOCUS_DOWN:
      bestCandidateRect.offset(0, -(focusedRect.height() + 1));
      break;
    default: // fall out
  }

  AccessibilityNodeInfoCompat closest = null;
  for (AccessibilityNodeInfoCompat focusable : mFocusables) {
    // Skip the currently-focused view.
    if (focusable.equals(focused) || focusable.equals(mRoot)) {
      continue;
    }

    Rect otherRect = new Rect();
    getAssumedRectInScreen(focusable, otherRect);

    if (isBetterCandidate(direction, focusedRect, otherRect, bestCandidateRect)) {
      bestCandidateRect.set(otherRect);
      closest = focusable;
    }
  }

  if (closest != null) {
    return AccessibilityNodeInfoCompat.obtain(closest);
  }

  return null;
}
 
Example 19
Source File: AccessibilityNodeInfoRef.java    From talkback with Apache License 2.0 4 votes vote down vote up
/** Creates a new instance of this class containing a new copy of {@code node}. */
public static AccessibilityNodeInfoRef obtain(AccessibilityNodeInfoCompat node) {
  return new AccessibilityNodeInfoRef(AccessibilityNodeInfoCompat.obtain(node), true);
}
 
Example 20
Source File: DirectionalTraversalStrategy.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Goes through root and its descendant nodes, sorting out the focusable nodes and the container
 * nodes for use in finding focus.
 *
 * @return whether the root is focusable or has focusable children in its hierarchy
 */
private boolean processNodes(AccessibilityNodeInfoCompat root, boolean forceRefresh) {
  if (root == null) {
    return false;
  }

  if (forceRefresh) {
    root.refresh();
  }

  Rect currentRect = new Rect();
  root.getBoundsInScreen(currentRect);

  // Determine if the node is inside mRootRect (within a fudge factor). If it is outside, we
  // will optimize by skipping its entire hierarchy.
  if (!Rect.intersects(currentRect, mRootRectPadded)) {
    return false;
  }

  AccessibilityNodeInfoCompat rootNode = AccessibilityNodeInfoCompat.obtain(root);
  mAllNodes.add(rootNode);

  // When we reach a node that supports web navigation, we traverse using the web navigation
  // actions, so we should not add any of its descendants to the list of focusable nodes.
  if (WebInterfaceUtils.hasNativeWebContent(rootNode)) {
    mFocusables.add(rootNode);
    return true;
  } else {
    boolean isFocusable =
        AccessibilityNodeInfoUtils.shouldFocusNode(rootNode, mSpeakingNodesCache);
    if (isFocusable) {
      mFocusables.add(rootNode);
    }

    boolean hasFocusableDescendants = false;
    int childCount = rootNode.getChildCount();
    for (int i = 0; i < childCount; ++i) {
      AccessibilityNodeInfoCompat child = rootNode.getChild(i);
      if (child != null) {
        hasFocusableDescendants |= processNodes(child, forceRefresh);
        child.recycle();
      }
    }

    if (hasFocusableDescendants) {
      mContainers.add(rootNode);
    }

    return isFocusable || hasFocusableDescendants;
  }
}