Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd0e5bec authored by sallyyuen's avatar sallyyuen
Browse files

Add AccessibilityNodeInfo.isTextSelectable

ACTION_SET_SELECTION is needed on all TextView nodes to reset the
cursor. But some non-edit texts are selectable and need to be
identified by a11y services. ACTION_SET_SELECTION has been on these
nodes for years so to avoid the complexity of breaking this, add a
node property.

Services should use ACTION_SET_SELECTION for nodes where
property=true

Bug: 62058901
Test: atest  AccessibilityTextTraversalTest, TextViewTest cts and
core (TextViewTest#testCutShouldNotThrowException keeps failing but
it's unrelated to this change)

Change-Id: Ia825dd806824e663e7fa7d274f51c6bf44f0c2c4
parent e39055a2
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -52266,6 +52266,7 @@ package android.view.accessibility {
    method public boolean isSelected();
    method public boolean isShowingHintText();
    method public boolean isTextEntryKey();
    method public boolean isTextSelectable();
    method public boolean isVisibleToUser();
    method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
    method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
@@ -52329,6 +52330,7 @@ package android.view.accessibility {
    method public void setStateDescription(@Nullable CharSequence);
    method public void setText(CharSequence);
    method public void setTextEntryKey(boolean);
    method public void setTextSelectable(boolean);
    method public void setTextSelection(int, int);
    method public void setTooltipText(@Nullable CharSequence);
    method public void setTouchDelegateInfo(@NonNull android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo);
+40 −1
Original line number Diff line number Diff line
@@ -710,6 +710,8 @@ public class AccessibilityNodeInfo implements Parcelable {

    private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000;

    private static final int BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE = 0x0800000;

    /**
     * Bits that provide the id of a virtual descendant of a view.
     */
@@ -2275,6 +2277,38 @@ public class AccessibilityNodeInfo implements Parcelable {
        setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
    }

    /**
     * Gets if the node has selectable text.
     *
     * <p>
     *     Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must
     *     also be selectable. But not all UIs will populate this field, so services should consider
     *     'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.
     * </p>
     *
     * @see #isEditable
     * @return True if the node has selectable text.
     */
    public boolean isTextSelectable() {
        return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE);
    }

    /**
     * Sets if the node has selectable text.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param selectableText True if the node has selectable text, false otherwise.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setTextSelectable(boolean selectableText) {
        setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE, selectableText);
    }

    /**
     * Gets if the node is editable.
     *
@@ -4327,10 +4361,14 @@ public class AccessibilityNodeInfo implements Parcelable {
                return "ACTION_CANCEL_DRAG";
            case R.id.accessibilityActionDragDrop:
                return "ACTION_DROP";
            default:
            default: {
                if (action == R.id.accessibilityActionShowSuggestions) {
                    return "ACTION_SHOW_SUGGESTIONS";
                }
                return "ACTION_UNKNOWN";
            }
        }
    }

    /**
     * Gets the human readable movement granularity symbolic name.
@@ -4462,6 +4500,7 @@ public class AccessibilityNodeInfo implements Parcelable {
        builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
        builder.append("; visible: ").append(isVisibleToUser());
        builder.append("; actions: ").append(mActions);
        builder.append("; isTextSelectable: ").append(isTextSelectable());

        return builder.toString();
    }
+1 −0
Original line number Diff line number Diff line
@@ -12197,6 +12197,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    EXTRA_DATA_RENDERING_INFO_KEY,
                    EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
            ));
            info.setTextSelectable(isTextSelectable());
        } else {
            info.setAvailableExtraData(Arrays.asList(
                    EXTRA_DATA_RENDERING_INFO_KEY
+1 −1
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ public class AccessibilityNodeInfoTest {

    // The number of flags held in boolean properties. Their values should also be double-checked
    // in the methods above.
    private static final int NUM_BOOLEAN_PROPERTIES = 23;
    private static final int NUM_BOOLEAN_PROPERTIES = 24;

    @Test
    public void testStandardActions_serializationFlagIsValid() {