androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat Java Examples

The following examples show how to use androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat. 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: ActionBuildingUtils.java    From talkback with Apache License 2.0 6 votes vote down vote up
/**
 * Returns whether the given {@link AccessibilityActionCompat} is supported by the given {@link
 * SwitchAccessNodeCompat}. If the action is a movement action, return {@code true} if the given
 * node is editable and has nonempty text. If the action is a selection action, return {@code
 * true} if the given node is an EditText and has nonempty text. Otherwise, all other supported
 * actions are possible.
 *
 * <p>Note: methods #hasMultipleActions and #getActionsForNode should be updated if the logic of
 * this one changes.
 *
 * @param action The action to check
 * @param nodeCompat The node for which we will check whether it is possible to perform the
 *     provided action
 * @return {@code true} if the given {@link AccessibilityActionCompat} is supported by the given
 *     {@link SwitchAccessNodeCompat}
 */
public static boolean isActionSupportedByNode(
    AccessibilityActionCompat action, SwitchAccessNodeCompat nodeCompat) {
  if (isActionSupportedBySwitchAccess(action)) {
    boolean isMovementOption = isMovementAction(action.getId());
    boolean isSelection = (action.getId() == AccessibilityNodeInfoCompat.ACTION_SET_SELECTION);
    if (!isMovementOption && !isSelection) {
      return true;
    }

    boolean textIsEmpty = TextUtils.isEmpty(nodeCompat.getText());
    boolean canMoveInNode = isMovementOption && nodeCompat.isEditable() && !textIsEmpty;
    if (canMoveInNode) {
      return true;
    }

    boolean isSelectingNonEmptyEditTextNode =
        isSelection && (Role.getRole(nodeCompat) == Role.ROLE_EDIT_TEXT) && !textIsEmpty;
    if (isSelectingNonEmptyEditTextNode) {
      return true;
    }
  }

  return false;
}
 
Example #2
Source File: BottomNavigationItemView.java    From material-components-android with Apache License 2.0 6 votes vote down vote up
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info) {
  super.onInitializeAccessibilityNodeInfo(info);
  if (badgeDrawable != null && badgeDrawable.isVisible()) {
    CharSequence customContentDescription = itemData.getTitle();
    if (!TextUtils.isEmpty(itemData.getContentDescription())) {
      customContentDescription = itemData.getContentDescription();
    }
    info.setContentDescription(
        customContentDescription + ", " + badgeDrawable.getContentDescription());
  }
  AccessibilityNodeInfoCompat infoCompat = AccessibilityNodeInfoCompat.wrap(info);
  infoCompat.setCollectionItemInfo(
      CollectionItemInfoCompat.obtain(
          /* rowIndex= */ 0,
          /* rowSpan= */ 1,
          /* columnIndex= */ getItemPosition(),
          /* columnSpan= */ 1,
          /* heading= */ false,
          /* selected= */ isSelected()));
  if (isSelected()) {
    infoCompat.setClickable(false);
    infoCompat.removeAction(AccessibilityActionCompat.ACTION_CLICK);
  }
  infoCompat.setRoleDescription(getResources().getString(R.string.item_view_role_description));
}
 
Example #3
Source File: AppBarLayout.java    From material-components-android with Apache License 2.0 6 votes vote down vote up
private void addActionToExpand(
    CoordinatorLayout parent,
    @NonNull final T appBarLayout,
    @NonNull AccessibilityActionCompat action,
    final boolean expand) {
  ViewCompat.replaceAccessibilityAction(
      parent,
      action,
      null,
      new AccessibilityViewCommand() {
        @Override
        public boolean perform(@NonNull View view, @Nullable CommandArguments arguments) {
          appBarLayout.setExpanded(expand);
          return true;
        }
      });
}
 
Example #4
Source File: TabLayout.java    From material-components-android with Apache License 2.0 6 votes vote down vote up
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info) {
  super.onInitializeAccessibilityNodeInfo(info);
  if (badgeDrawable != null && badgeDrawable.isVisible()) {
    CharSequence customContentDescription = getContentDescription();
    info.setContentDescription(
        customContentDescription + ", " + badgeDrawable.getContentDescription());
  }
  AccessibilityNodeInfoCompat infoCompat = AccessibilityNodeInfoCompat.wrap(info);
  infoCompat.setCollectionItemInfo(
      CollectionItemInfoCompat.obtain(
          /* rowIndex= */ 0,
          /* rowSpan= */ 1,
          /* columnIndex= */ tab.getPosition(),
          /* columnSpan= */ 1,
          /* heading= */ false,
          /* selected= */ isSelected()));
  if (isSelected()) {
    infoCompat.setClickable(false);
    infoCompat.removeAction(AccessibilityActionCompat.ACTION_CLICK);
  }
  infoCompat.setRoleDescription("Tab");
}
 
Example #5
Source File: Chip.java    From material-components-android with Apache License 2.0 6 votes vote down vote up
@Override
protected void onPopulateNodeForVirtualView(
    int virtualViewId, @NonNull AccessibilityNodeInfoCompat node) {
  if (virtualViewId == CLOSE_ICON_VIRTUAL_ID) {
    CharSequence closeIconContentDescription = getCloseIconContentDescription();
    if (closeIconContentDescription != null) {
      node.setContentDescription(closeIconContentDescription);
    } else {
      CharSequence chipText = getText();
      node.setContentDescription(
          getContext()
              .getString(
                  R.string.mtrl_chip_close_icon_content_description,
                  !TextUtils.isEmpty(chipText) ? chipText : "")
              .trim());
    }
    node.setBoundsInParent(getCloseIconTouchBoundsInt());
    node.addAction(AccessibilityActionCompat.ACTION_CLICK);
    node.setEnabled(isEnabled());
  } else {
    node.setContentDescription("");
    node.setBoundsInParent(EMPTY_BOUNDS);
  }
}
 
Example #6
Source File: ActionVariables.java    From talkback with Apache License 2.0 5 votes vote down vote up
/**
 * Constructs a ActionVariables, which contains context variables to help generate feedback for an
 * accessibility action.
 *
 * @param action The accessibility action for which we are generating feedback.
 */
public ActionVariables(
    Context context, ParseTree.VariableDelegate parent, AccessibilityActionCompat action) {
  if (action == null) {
    throw new IllegalArgumentException("action cannot be null");
  }
  mContext = context;
  mParentVariables = parent;
  mAction = action;
}
 
Example #7
Source File: ActionBuildingUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
private static boolean isActionSupportedBySwitchAccess(AccessibilityActionCompat action) {
  // White-listed framework actions
  if (action.getId() <= SYSTEM_ACTION_MAX) {
    return FRAMEWORK_ACTIONS.contains(action.getId());
  }
  // Support custom actions with proper labels.
  return !TextUtils.isEmpty(action.getLabel());
}
 
Example #8
Source File: ShowActionsMenuNode.java    From talkback with Apache License 2.0 5 votes vote down vote up
@Override
public List<AccessibilityActionCompat> getActionList() {
  List<AccessibilityActionCompat> actions = new ArrayList<>();
  for (SwitchAccessNodeCompat node : nodeCompats) {
    actions.addAll(node.getActionList());
  }
  return actions;
}
 
Example #9
Source File: SwitchAccessAction.java    From talkback with Apache License 2.0 5 votes vote down vote up
@Override
@Nullable
// LINT.IfChange(generateInverseAction)
public TimelineAction generateInverseAction() {
  Bundle args = new Bundle();
  switch (getId()) {
    case AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
    case AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
    case AccessibilityNodeInfoCompat.ACTION_SET_SELECTION:
      // If previousSelectionStart == -1 or previousSelectionEnd == -1, performing this action
      // does nothing.
      args.putInt(
          AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, previousSelectionStart);
      args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, previousSelectionEnd);
      return new SwitchAccessAction(
          nodeCompat, AccessibilityActionCompat.ACTION_SET_SELECTION, args);
    case AccessibilityNodeInfoCompat.ACTION_CUT:
    case AccessibilityNodeInfoCompat.ACTION_PASTE:
    case TextEditingUtils.ACTION_DELETE_TEXT:
      args.putInt(
          AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, previousSelectionStart);
      args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, previousSelectionEnd);
      args.putCharSequence(
          AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, previousTextContent);
      return new SwitchAccessAction(nodeCompat, AccessibilityActionCompat.ACTION_SET_TEXT, args);
    default:
      // Action is not undoable.
      return null;
  }
}
 
Example #10
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
/**
 * 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) {
  // 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;
}
 
Example #11
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 #12
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
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;
}
 
Example #13
Source File: SwipeDismissBehavior.java    From material-components-android with Apache License 2.0 5 votes vote down vote up
private void updateAccessibilityActions(View child) {
  ViewCompat.removeAccessibilityAction(child, AccessibilityNodeInfoCompat.ACTION_DISMISS);
  if (canSwipeDismissView(child)) {
    ViewCompat.replaceAccessibilityAction(
        child,
        AccessibilityActionCompat.ACTION_DISMISS,
        null,
        new AccessibilityViewCommand() {
          @Override
          public boolean perform(@NonNull View view, @Nullable CommandArguments arguments) {
            if (canSwipeDismissView(view)) {
              final boolean isRtl =
                  ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
              boolean dismissToLeft =
                  (swipeDirection == SWIPE_DIRECTION_START_TO_END && isRtl)
                      || (swipeDirection == SWIPE_DIRECTION_END_TO_START && !isRtl);
              int offset = dismissToLeft ? -view.getWidth() : view.getWidth();
              ViewCompat.offsetLeftAndRight(view, offset);
              view.setAlpha(0f);
              if (listener != null) {
                listener.onDismiss(view);
              }
              return true;
            }
            return false;
          }
        });
  }
}
 
Example #14
Source File: BottomSheetBehavior.java    From material-components-android with Apache License 2.0 5 votes vote down vote up
private void addAccessibilityActionForState(
    V child, AccessibilityActionCompat action, final int state) {
  ViewCompat.replaceAccessibilityAction(
      child,
      action,
      null,
      new AccessibilityViewCommand() {
        @Override
        public boolean perform(@NonNull View view, @Nullable CommandArguments arguments) {
          setState(state);
          return true;
        }
      });
}
 
Example #15
Source File: AccessibilityUtils.java    From material-components-android with Apache License 2.0 5 votes vote down vote up
private static ArrayList<AccessibilityActionCompat> getActionList(View view) {
  @SuppressWarnings("unchecked")
  ArrayList<AccessibilityActionCompat> actions =
      (ArrayList<AccessibilityActionCompat>) view.getTag(R.id.tag_accessibility_actions);
  if (actions == null) {
    actions = new ArrayList<>();
  }
  return actions;
}
 
Example #16
Source File: AccessibilityUtils.java    From material-components-android with Apache License 2.0 5 votes vote down vote up
/** Used to check ViewCompat.addAccessibilityAction */
public static boolean hasAction(View view, int actionId) {
  ArrayList<AccessibilityActionCompat> actions = getActionList(view);
  for (int i = 0; i < actions.size(); i++) {
    if (actions.get(i).getId() == actionId) {
      return true;
    }
  }
  return false;
}
 
Example #17
Source File: AccessibilityUtils.java    From material-components-android with Apache License 2.0 5 votes vote down vote up
/** Used to check onInitializeAccessibilityNodeInfo */
public static boolean hasAction(
    AccessibilityNodeInfoCompat info, AccessibilityActionCompat action) {
  List<AccessibilityActionCompat> actions = info.getActionList();
  for (int i = 0; i < actions.size(); i++) {
    if (actions.get(i).getId() == action.getId()) {
      return true;
    }
  }
  return false;
}
 
Example #18
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 #19
Source File: RuleCustomAction.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Populates a menu with the context menu items for a node, searching up its ancestor hierarchy if
 * the current node has no custom actions.
 *
 * @param service The parent service.
 * @param menuItemBuilder builder to create menu items
 * @param node The node to process
 * @param includeAncestors sets to {@code false} not to search its ancestor
 */
private void populateMenuItemsForNode(
    TalkBackService service,
    ContextMenuItemBuilder menuItemBuilder,
    AccessibilityNodeInfoCompat node,
    List<ContextMenuItem> menu,
    boolean includeAncestors) {
  if (node == null) {
    return;
  }

  for (AccessibilityActionCompat action : node.getActionList()) {
    CharSequence label = "";
    int id = action.getId();
    // On Android O, sometime TalkBack get fails on performing actions (mostly on notification
    // shelf). And deferring the action make the but unreproducible. See  for details.
    boolean deferToWindowsSrable = false;

    if (AccessibilityNodeInfoUtils.isCustomAction(action)) {
      label = action.getLabel();
    } else if (id == AccessibilityNodeInfoCompat.ACTION_DISMISS) {
      label = service.getString(R.string.title_action_dismiss);
      deferToWindowsSrable = true;
    } else if (id == AccessibilityNodeInfoCompat.ACTION_EXPAND) {
      label = service.getString(R.string.title_action_expand);
      deferToWindowsSrable = true;
    } else if (id == AccessibilityNodeInfoCompat.ACTION_COLLAPSE) {
      label = service.getString(R.string.title_action_collapse);
      deferToWindowsSrable = true;
    }

    if (TextUtils.isEmpty(label)) {
      continue;
    }

    ContextMenuItem item =
        menuItemBuilder.createMenuItem(service, Menu.NONE, id, Menu.NONE, label);
    item.setOnMenuItemClickListener(
        new CustomMenuItem(id, AccessibilityNodeInfoCompat.obtain(node)));
    if ((Build.VERSION.SDK_INT == VERSION_CODES.O || Build.VERSION.SDK_INT == VERSION_CODES.O_MR1)
        && deferToWindowsSrable) {
      item.setDeferredType(DeferredType.WINDOWS_STABLE);
    }
    item.setCheckable(false);
    menu.add(item);
  }

  if (!includeAncestors) {
    return;
  }

  if (menu.isEmpty()) {
    populateMenuItemsForNode(
        service, menuItemBuilder, node.getParent(), menu, /* includeAncestors= */ true);
  }
}
 
Example #20
Source File: RuleCustomAction.java    From talkback with Apache License 2.0 4 votes vote down vote up
@Override
public boolean accept(TalkBackService service, AccessibilityNodeInfoCompat node) {
  List<AccessibilityActionCompat> actions = node.getActionList();
  return actions != null && !actions.isEmpty();
}
 
Example #21
Source File: TreeScanLeafNode.java    From talkback with Apache License 2.0 4 votes vote down vote up
/** Returns the list of actions that can be performed on this node. */
public List<AccessibilityActionCompat> getActionList() {
  return Collections.emptyList();
}
 
Example #22
Source File: OptionScanHighlighter.java    From talkback with Apache License 2.0 4 votes vote down vote up
@Override
public void run() {
  int[] layoutCoordinates = new int[2];
  for (TreeScanLeafNode node : nodes) {
    Rect rect = node.getRectForNodeHighlight();
    if (rect == null) {
      continue;
    }
    int halfStrokeWidth = (int) highlightPaint.getStrokeWidth() / 2;
    GradientDrawable mainHighlightDrawable = getHighlightDrawable(halfStrokeWidth, rect);
    @KeyFor("MAIN_TO_OUTER_HIGHLIGHT_COLOR_MAP")
    int mainPaintColor = highlightPaint.getColor();
    mainHighlightDrawable.setStroke(halfStrokeWidth, mainPaintColor);

    GradientDrawable outerHighlightDrawable = null;
    if (MAIN_TO_OUTER_HIGHLIGHT_COLOR_MAP.containsKey(mainPaintColor)) {
      outerHighlightDrawable = getHighlightDrawable(halfStrokeWidth, rect);
      outerHighlightDrawable.setStroke(
          halfStrokeWidth / 2, MAIN_TO_OUTER_HIGHLIGHT_COLOR_MAP.get(mainPaintColor));
    }

    // Determine which scroll arrows we should show.
    boolean shouldShowUpArrow = false;
    boolean shouldShowDownArrow = false;
    boolean shouldShowRightArrow = false;
    boolean shouldShowLeftArrow = false;
    boolean supportsScrollForward = false;
    boolean supportsScrollBackward = false;
    if (FeatureFlags.scrollArrows()
        && (VERSION.SDK_INT >= VERSION_CODES.N)
        && node.isScrollable()) {
      for (AccessibilityActionCompat action : node.getActionList()) {
        if (action.getId() == AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD) {
          supportsScrollBackward = true;
        } else if (action.getId() == AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD) {
          supportsScrollForward = true;
        } else if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
          if (action.getId() == AccessibilityActionCompat.ACTION_SCROLL_UP.getId()) {
            shouldShowUpArrow = true;
          } else if (action.getId() == AccessibilityActionCompat.ACTION_SCROLL_DOWN.getId()) {
            shouldShowDownArrow = true;
          } else if (action.getId() == AccessibilityActionCompat.ACTION_SCROLL_RIGHT.getId()) {
            shouldShowRightArrow = true;
          } else if (action.getId() == AccessibilityActionCompat.ACTION_SCROLL_LEFT.getId()) {
            shouldShowLeftArrow = true;
          }
        }
      }

      // If only the less granular version of a scroll action is supported, show the possible
      // scroll directions for that scroll action.
      if (supportsScrollForward && !shouldShowDownArrow && !shouldShowRightArrow) {
        shouldShowDownArrow = true;
        shouldShowRightArrow = true;
      }
      if (supportsScrollBackward && !shouldShowUpArrow && !shouldShowLeftArrow) {
        shouldShowUpArrow = true;
        shouldShowLeftArrow = true;
      }
    }

    ImageView imageView = new ImageView(overlayController.getContext());
    imageView.setBackground(
        getFinalHighlightDrawable(
            shouldShowUpArrow,
            shouldShowDownArrow,
            shouldShowRightArrow,
            shouldShowLeftArrow,
            mainHighlightDrawable,
            outerHighlightDrawable));

    // Align image with node we're highlighting
    final RelativeLayout.LayoutParams layoutParams =
        new RelativeLayout.LayoutParams(rect.width(), rect.height());
    layoutParams.leftMargin = rect.left - layoutCoordinates[0];
    layoutParams.topMargin = rect.top - layoutCoordinates[1];
    imageView.setLayoutParams(layoutParams);
    overlayController.addViewAndShow(imageView);
  }
}
 
Example #23
Source File: ActionBuildingUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Get the {@link SwitchAccessActionBase}s that the given {@link SwitchAccessNodeCompat} can
 * perform.
 *
 * <p>Note: methods #hasMultipleActions and #isActionSupportedByNode should be updated if the
 * logic of this one changes.
 *
 * @param service The {@link AccessibilityService} used to check whether auto-select is enabled
 * @param nodeCompat The {@link SwitchAccessNodeCompat} to get actions for
 * @param actionTimeline The {@link SwitchAccessActionTimeline} used for the given {@code
 *     nodeCompat) if it does not already have one. The timeline is used to check if the {@code
 *     nodeCompat} has undo or redo actions
 * @return A list of {@link SwitchAccessActionBase}s that the given {@link SwitchAccessNodeCompat}
 *     can perform
 */
public static List<SwitchAccessActionBase> getActionsForNode(
    AccessibilityService service,
    SwitchAccessNodeCompat nodeCompat,
    SwitchAccessActionTimeline actionTimeline) {
  // Get the latest text in this node. If the user types something without using the switches, the
  // node doesn't get updated automatically.
  nodeCompat.refresh();
  List<SwitchAccessActionBase> actions = new ArrayList<>();
  List<AccessibilityActionCompat> originalActions = nodeCompat.getActionList();
  boolean autoselectEnabled = SwitchAccessPreferenceUtils.isAutoselectEnabled(service);
  for (AccessibilityActionCompat action : originalActions) {
    if (autoselectEnabled && (action.getId() == AccessibilityNodeInfoCompat.ACTION_CLICK)) {
      actions.clear();
      actions.add(new SwitchAccessAction(nodeCompat, action));
      return actions;
    }
    if (isActionSupportedBySwitchAccess(action)) {
      if (isMovementAction(action.getId())) {
        actions.addAll(getSwitchAccessMovementActionsForNode(nodeCompat, action));
      } else if (action.getId() == AccessibilityNodeInfoCompat.ACTION_SET_SELECTION) {
        actions.addAll(getSwitchAccessActionsForSetSelectionAction(nodeCompat, action));
      } else {
        actions.add(new SwitchAccessAction(nodeCompat, action));
      }
    }
  }

  // Get a copy of nodeCompat so that if it is recycled before the corresponding timeline is
  // removed, the timeline doesn't act on a recycled node.
  SwitchAccessNodeCompat node = nodeCompat.obtainCopy();
  SwitchAccessActionTimeline switchAccessActionTimeline =
      (SwitchAccessActionTimeline)
          UndoRedoManager.getInstance(RecycleBehavior.DO_RECYCLE_NODES)
              .getTimelineForNodeCompat(node, actionTimeline);
  if (switchAccessActionTimeline == null) {
    node.recycle();
  } else if (node.isEditable()) {
    if (switchAccessActionTimeline.canPerformUndo()) {
      actions.add(new SwitchAccessAction(node, TextEditingUtils.ACTION_UNDO));
    }
    if (switchAccessActionTimeline.canPerformRedo()) {
      actions.add(new SwitchAccessAction(node, TextEditingUtils.ACTION_REDO));
    }
  }

  return actions;
}
 
Example #24
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
public static boolean isCustomAction(AccessibilityActionCompat action) {
  return action.getId() > SYSTEM_ACTION_MAX;
}
 
Example #25
Source File: SwitchAccessActionGroup.java    From talkback with Apache License 2.0 2 votes vote down vote up
/**
 * @param actionCompat The {@link AccessibilityActionCompat} whose id and label identify this
 *     action group
 */
public SwitchAccessActionGroup(AccessibilityActionCompat actionCompat) {
  this(actionCompat.getId(), actionCompat.getLabel());
}
 
Example #26
Source File: SwitchAccessAction.java    From talkback with Apache License 2.0 2 votes vote down vote up
/**
 * @param nodeCompat The {@link SwitchAccessNodeCompat} to perform this action on
 * @param actionCompat The {@link AccessibilityActionCompat} whose id and label to use to identify
 *     this action
 * @param args Additional arguments to use when performing this action
 */
public SwitchAccessAction(
    SwitchAccessNodeCompat nodeCompat, AccessibilityActionCompat actionCompat, Bundle args) {
  this(nodeCompat, actionCompat.getId(), actionCompat.getLabel(), args);
}
 
Example #27
Source File: SwitchAccessAction.java    From talkback with Apache License 2.0 2 votes vote down vote up
/**
 * @param nodeCompat The {@link SwitchAccessNodeCompat} to perform this action on
 * @param actionCompat The {@link AccessibilityActionCompat} whose id and label to use to identify
 *     this action
 */
public SwitchAccessAction(
    SwitchAccessNodeCompat nodeCompat, AccessibilityActionCompat actionCompat) {
  this(nodeCompat, actionCompat, Bundle.EMPTY);
}