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

Commit 5a2f6bee authored by Svetoslav's avatar Svetoslav Committed by Android Git Automerger
Browse files

am f48a2d30: Merge "Add accessibility actions for text editing."

# Via Android (Google) Code Review (1) and Svetoslav (1)
* commit 'f48a2d30':
  Add accessibility actions for text editing.
parents 62d59521 f48a2d30
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -26352,21 +26352,28 @@ package android.view.accessibility {
    method public void setVisibleToUser(boolean);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
    field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
    field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
    field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
    field public static final int ACTION_CLICK = 16; // 0x10
    field public static final int ACTION_COPY = 16384; // 0x4000
    field public static final int ACTION_CUT = 65536; // 0x10000
    field public static final int ACTION_FOCUS = 1; // 0x1
    field public static final int ACTION_LONG_CLICK = 32; // 0x20
    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
    field public static final int ACTION_PASTE = 32768; // 0x8000
    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
    field public static final int ACTION_SELECT = 4; // 0x4
    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
    field public static final android.os.Parcelable.Creator CREATOR;
    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
    field public static final int FOCUS_INPUT = 1; // 0x1
+57 −24
Original line number Diff line number Diff line
@@ -1562,9 +1562,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    int mAccessibilityViewId = NO_ID;
    /**
     * @hide
     */
    private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
    /**
@@ -2516,8 +2513,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    /**
     * The undefined cursor position.
     *
     * @hide
     */
    private static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1;
    public static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1;
    /**
     * Indicates that the screen has changed state and is now off.
@@ -7009,21 +7008,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                if (arguments != null) {
                    final int granularity = arguments.getInt(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
                    return nextAtGranularity(granularity);
                    final boolean extendSelection = arguments.getBoolean(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);
                    return nextAtGranularity(granularity, extendSelection);
                }
            } break;
            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: {
                if (arguments != null) {
                    final int granularity = arguments.getInt(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
                    return previousAtGranularity(granularity);
                    final boolean extendSelection = arguments.getBoolean(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);
                    return previousAtGranularity(granularity, extendSelection);
                }
            } break;
        }
        return false;
    }
    private boolean nextAtGranularity(int granularity) {
    private boolean nextAtGranularity(int granularity, boolean extendSelection) {
        CharSequence text = getIterableTextForAccessibility();
        if (text == null || text.length() == 0) {
            return false;
@@ -7032,21 +7035,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        if (iterator == null) {
            return false;
        }
        final int current = getAccessibilityCursorPosition();
        int current = getAccessibilitySelectionEnd();
        if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
            current = 0;
        }
        final int[] range = iterator.following(current);
        if (range == null) {
            return false;
        }
        final int start = range[0];
        final int end = range[1];
        setAccessibilityCursorPosition(end);
        if (extendSelection && isAccessibilitySelectionExtendable()) {
            int selectionStart = getAccessibilitySelectionStart();
            if (selectionStart == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
                selectionStart = start;
            }
            setAccessibilitySelection(selectionStart, end);
        } else {
            setAccessibilitySelection(end, end);
        }
        sendViewTextTraversedAtGranularityEvent(
                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                granularity, start, end);
        return true;
    }
    private boolean previousAtGranularity(int granularity) {
    private boolean previousAtGranularity(int granularity, boolean extendSelection) {
        CharSequence text = getIterableTextForAccessibility();
        if (text == null || text.length() == 0) {
            return false;
@@ -7055,15 +7069,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        if (iterator == null) {
            return false;
        }
        int current = getAccessibilityCursorPosition();
        int current = getAccessibilitySelectionStart();
        if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
            current = text.length();
            setAccessibilityCursorPosition(current);
        } else if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
            // When traversing by character we always put the cursor after the character
            // to ease edit and have to compensate before asking the for previous segment.
            current--;
            setAccessibilityCursorPosition(current);
        }
        final int[] range = iterator.preceding(current);
        if (range == null) {
@@ -7071,11 +7079,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        final int start = range[0];
        final int end = range[1];
        // Always put the cursor after the character to ease edit.
        if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
            setAccessibilityCursorPosition(end);
        if (extendSelection && isAccessibilitySelectionExtendable()) {
            int selectionEnd = getAccessibilitySelectionEnd();
            if (selectionEnd == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
                selectionEnd = end;
            }
            setAccessibilitySelection(start, selectionEnd);
        } else {
            setAccessibilityCursorPosition(start);
            setAccessibilitySelection(start, start);
        }
        sendViewTextTraversedAtGranularityEvent(
                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
@@ -7094,18 +7105,40 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return getContentDescription();
    }
    /**
     * Gets whether accessibility selection can be extended.
     *
     * @return If selection is extensible.
     *
     * @hide
     */
    public boolean isAccessibilitySelectionExtendable() {
        return false;
    }
    /**
     * @hide
     */
    public int getAccessibilityCursorPosition() {
    public int getAccessibilitySelectionStart() {
        return mAccessibilityCursorPosition;
    }
    /**
     * @hide
     */
    public void setAccessibilityCursorPosition(int position) {
        mAccessibilityCursorPosition = position;
    public int getAccessibilitySelectionEnd() {
        return getAccessibilitySelectionStart();
    }
    /**
     * @hide
     */
    public void setAccessibilitySelection(int start, int end) {
        if (start >= 0 && start == end && end <= getIterableTextForAccessibility().length()) {
            mAccessibilityCursorPosition = start;
        } else {
            mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
        }
    }
    private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
+98 −6
Original line number Diff line number Diff line
@@ -131,16 +131,22 @@ public class AccessibilityNodeInfo implements Parcelable {
     * at a given movement granularity. For example, move to the next character,
     * word, etc.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
     * <strong>Example:</strong>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
     * <strong>Example:</strong> Move to the previous character and do not extend selection.
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
     *           false);
     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
     * </code></pre></p>
     * </p>
     *
     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
     *
     * @see #setMovementGranularities(int)
     * @see #getMovementGranularities()
     *
@@ -157,17 +163,23 @@ public class AccessibilityNodeInfo implements Parcelable {
     * at a given movement granularity. For example, move to the next character,
     * word, etc.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
     * <strong>Example:</strong>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
     * <strong>Example:</strong> Move to the next character and do not extend selection.
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
     *           false);
     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
     *           arguments);
     * </code></pre></p>
     * </p>
     *
     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
     *
     * @see #setMovementGranularities(int)
     * @see #getMovementGranularities()
     *
@@ -219,6 +231,41 @@ public class AccessibilityNodeInfo implements Parcelable {
     */
    public static final int ACTION_SCROLL_BACKWARD = 0x00002000;

    /**
     * Action to copy the current selection to the clipboard.
     */
    public static final int ACTION_COPY = 0x00004000;

    /**
     * Action to paste the current clipboard content.
     */
    public static final int ACTION_PASTE = 0x00008000;

    /**
     * Action to cut the current selection and place it to the clipboard.
     */
    public static final int ACTION_CUT = 0x00010000;

    /**
     * Action to set the selection. Performing this action with no arguments
     * clears the selection.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
     * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
     * <strong>Example:</strong>
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
     * </code></pre></p>
     * </p>
     *
     * @see #ACTION_ARGUMENT_SELECTION_START_INT
     * @see #ACTION_ARGUMENT_SELECTION_END_INT
     */
    public static final int ACTION_SET_SELECTION = 0x00020000;

    /**
     * Argument for which movement granularity to be used when traversing the node text.
     * <p>
@@ -226,6 +273,9 @@ public class AccessibilityNodeInfo implements Parcelable {
     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
     * </p>
     *
     * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
     * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
     */
    public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
            "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
@@ -237,10 +287,52 @@ public class AccessibilityNodeInfo implements Parcelable {
     * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
     *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
     * </p>
     *
     * @see #ACTION_NEXT_HTML_ELEMENT
     * @see #ACTION_PREVIOUS_HTML_ELEMENT
     */
    public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
            "ACTION_ARGUMENT_HTML_ELEMENT_STRING";

    /**
     * Argument for whether when moving at granularity to extend the selection
     * or to move it otherwise.
     * <p>
     * <strong>Type:</strong> boolean<br>
     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
     * </p>
     *
     * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
     * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
     */
    public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
            "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";

    /**
     * Argument for specifying the selection start.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
     * </p>
     *
     * @see #ACTION_SET_SELECTION
     */
    public static final String ACTION_ARGUMENT_SELECTION_START_INT =
            "ACTION_ARGUMENT_SELECTION_START_INT";

    /**
     * Argument for specifying the selection end.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
     * </p>
     *
     * @see #ACTION_SET_SELECTION
     */
    public static final String ACTION_ARGUMENT_SELECTION_END_INT =
            "ACTION_ARGUMENT_SELECTION_END_INT";

    /**
     * The input focus.
     */
+104 −11
Original line number Diff line number Diff line
@@ -7985,6 +7985,80 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
                    | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
        }
        if (isFocused()) {
            if (canSelectText()) {
                info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
            }
            if (canCopy()) {
                info.addAction(AccessibilityNodeInfo.ACTION_COPY);
            }
            if (canPaste()) {
                info.addAction(AccessibilityNodeInfo.ACTION_PASTE);
            }
            if (canCut()) {
                info.addAction(AccessibilityNodeInfo.ACTION_CUT);
            }
        }
    }

    @Override
    public boolean performAccessibilityAction(int action, Bundle arguments) {
        switch (action) {
            case AccessibilityNodeInfo.ACTION_COPY: {
                if (isFocused() && canCopy()) {
                    if (onTextContextMenuItem(ID_COPY)) {
                        notifyAccessibilityStateChanged();
                        return true;
                    }
                }
            } return false;
            case AccessibilityNodeInfo.ACTION_PASTE: {
                if (isFocused() && canPaste()) {
                    if (onTextContextMenuItem(ID_PASTE)) {
                        notifyAccessibilityStateChanged();
                        return true;
                    }
                }
            } return false;
            case AccessibilityNodeInfo.ACTION_CUT: {
                if (isFocused() && canCut()) {
                    if (onTextContextMenuItem(ID_CUT)) {
                        notifyAccessibilityStateChanged();
                        return true;
                    }
                }
            } return false;
            case AccessibilityNodeInfo.ACTION_SET_SELECTION: {
                if (isFocused() && canSelectText()) {
                    final int start = (arguments != null) ? arguments.getInt(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1;
                    final int end = (arguments != null) ? arguments.getInt(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1;
                    CharSequence text = getIterableTextForAccessibility();
                    if (text == null) {
                        return false;
                    }
                    // No arguments clears the selection.
                    if (start == end && end == -1) {
                        Selection.removeSelection((Spannable) text);
                        notifyAccessibilityStateChanged();
                        return true;
                    }
                    if (start >= 0 && start <= end && end <= text.length()) {
                        Selection.setSelection((Spannable) text, start, end);
                        // Make sure selection mode is engaged.
                        if (mEditor != null) {
                            mEditor.startSelectionActionMode();
                        }
                        notifyAccessibilityStateChanged();
                        return true;
                    }
                }
            } return false;
            default: {
                return super.performAccessibilityAction(action, arguments);
            }
        }
    }

    @Override
@@ -8554,32 +8628,51 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     * @hide
     */
    @Override
    public int getAccessibilityCursorPosition() {
    public int getAccessibilitySelectionStart() {
        if (TextUtils.isEmpty(getContentDescription())) {
            final int selectionStart = getSelectionStart();
            if (selectionStart >= 0) {
                return selectionStart;
            }
        }
        return ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
    }

    /**
     * @hide
     */
    public boolean isAccessibilitySelectionExtendable() {
        return true;
    }

    /**
     * @hide
     */
    @Override
    public int getAccessibilitySelectionEnd() {
        if (TextUtils.isEmpty(getContentDescription())) {
            final int selectionEnd = getSelectionEnd();
            if (selectionEnd >= 0) {
                return selectionEnd;
            }
        }
        return super.getAccessibilityCursorPosition();
        return ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
    }

    /**
     * @hide
     */
    @Override
    public void setAccessibilityCursorPosition(int index) {
        if (getAccessibilityCursorPosition() == index) {
    public void setAccessibilitySelection(int start, int end) {
        if (getAccessibilitySelectionStart() == start
                && getAccessibilitySelectionEnd() == end) {
            return;
        }
        if (TextUtils.isEmpty(getContentDescription())) {
            if (index >= 0 && index <= mText.length()) {
                Selection.setSelection((Spannable) mText, index);
            } else {
                Selection.removeSelection((Spannable) mText);
            }
        CharSequence text = getIterableTextForAccessibility();
        if (start >= 0 && start <= end && end <= text.length()) {
            Selection.setSelection((Spannable) text, start, end);
        } else {
            super.setAccessibilityCursorPosition(index);
            Selection.removeSelection((Spannable) text);
        }
    }

+5 −1
Original line number Diff line number Diff line
@@ -2225,7 +2225,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
            | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT
            | AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
            | AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
            | AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
            | AccessibilityNodeInfo.ACTION_COPY
            | AccessibilityNodeInfo.ACTION_PASTE
            | AccessibilityNodeInfo.ACTION_CUT
            | AccessibilityNodeInfo.ACTION_SET_SELECTION;

        private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
            AccessibilityEvent.TYPE_VIEW_CLICKED