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

Commit 39f2aee6 authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Updating the behaviour of accessibility text iterators.

1. Iterators were skipping content on reversing direction.

2. The cursor was positioned at the beginning of the next text segment
   when moving forward and at end of the previous text segment when moving
   backwards. This is incorrect and now the cursor is positioned at the
   end of the segment when moving forward and at the beginning when moving
   backward.

3. The cursor position was not properly set when reaching the end/start
   of the text.

4. The iterators were reporting strictly the next/previous segment even
   if the cursor is within such a segment. Thus, when traversing some
   content may be skipped. Now moving forward moves the selection to
   the next segment end and the start position is either the old index
   if it was within a segment or the start of the segment. Same in
   reverse.

bug:6575099

Change-Id: Ib48a649cec53910339baf831a75e26440be6e576
parent a587b890
Loading
Loading
Loading
Loading
+74 −94
Original line number Original line Diff line number Diff line
@@ -47,7 +47,6 @@ public final class AccessibilityIterators {
     * @hide
     * @hide
     */
     */
    public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
    public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
        protected static final int DONE = -1;


        protected String mText;
        protected String mText;


@@ -104,20 +103,20 @@ public final class AccessibilityIterators {
            if (offset >= textLegth) {
            if (offset >= textLegth) {
                return null;
                return null;
            }
            }
            int start = -1;
            int start = offset;
            if (offset < 0) {
                offset = 0;
                if (mImpl.isBoundary(offset)) {
                    start = offset;
                }
            }
            if (start < 0) {
            if (start < 0) {
                start = mImpl.following(offset);
                start = 0;
            }
            }
            if (start < 0) {
            while (!mImpl.isBoundary(start)) {
                start = mImpl.following(start);
                if (start == BreakIterator.DONE) {
                    return null;
                    return null;
                }
                }
            }
            final int end = mImpl.following(start);
            final int end = mImpl.following(start);
            if (end == BreakIterator.DONE) {
                return null;
            }
            return getRange(start, end);
            return getRange(start, end);
        }
        }


@@ -130,20 +129,20 @@ public final class AccessibilityIterators {
            if (offset <= 0) {
            if (offset <= 0) {
                return null;
                return null;
            }
            }
            int end = -1;
            int end = offset;
            if (offset > mText.length()) {
            if (end > textLegth) {
                offset = mText.length();
                end = textLegth;
                if (mImpl.isBoundary(offset)) {
                    end = offset;
            }
            }
            while (!mImpl.isBoundary(end)) {
                end = mImpl.preceding(end);
                if (end == BreakIterator.DONE) {
                    return null;
                }
                }
            if (end < 0) {
                end = mImpl.preceding(offset);
            }
            }
            if (end < 0) {
            final int start = mImpl.preceding(end);
            if (start == BreakIterator.DONE) {
                return null;
                return null;
            }
            }
            final int start = mImpl.preceding(end);
            return getRange(start, end);
            return getRange(start, end);
        }
        }


@@ -195,25 +194,20 @@ public final class AccessibilityIterators {
            if (offset >= mText.length()) {
            if (offset >= mText.length()) {
                return null;
                return null;
            }
            }
            int start = -1;
            int start = offset;
            if (offset < 0) {
                offset = 0;
                if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) {
                    start = offset;
                }
            }
            if (start < 0) {
            if (start < 0) {
                while ((offset = mImpl.following(offset)) != DONE) {
                start = 0;
                    if (isLetterOrDigit(offset)) {
                        start = offset;
                        break;
            }
            }
            while (!isLetterOrDigit(start) && !isStartBoundary(start)) {
                start = mImpl.following(start);
                if (start == BreakIterator.DONE) {
                    return null;
                }
                }
            }
            }
            if (start < 0) {
            final int end = mImpl.following(start);
            if (end == BreakIterator.DONE || !isEndBoundary(end)) {
                return null;
                return null;
            }
            }
            final int end = mImpl.following(start);
            return getRange(start, end);
            return getRange(start, end);
        }
        }


@@ -226,28 +220,33 @@ public final class AccessibilityIterators {
            if (offset <= 0) {
            if (offset <= 0) {
                return null;
                return null;
            }
            }
            int end = -1;
            int end = offset;
            if (offset > mText.length()) {
            if (end > textLegth) {
                offset = mText.length();
                end = textLegth;
                if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) {
                    end = offset;
                }
            }
            if (end < 0) {
                while ((offset = mImpl.preceding(offset)) != DONE) {
                    if (offset > 0 && isLetterOrDigit(offset - 1)) {
                        end = offset;
                        break;
            }
            }
            while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) {
                end = mImpl.preceding(end);
                if (end == BreakIterator.DONE) {
                    return null;
                }
                }
            }
            }
            if (end < 0) {
            final int start = mImpl.preceding(end);
            if (start == BreakIterator.DONE || !isStartBoundary(start)) {
                return null;
                return null;
            }
            }
            final int start = mImpl.preceding(end);
            return getRange(start, end);
            return getRange(start, end);
        }
        }


        private boolean isStartBoundary(int index) {
            return isLetterOrDigit(index)
                && (index == 0 || !isLetterOrDigit(index - 1));
        }

        private boolean isEndBoundary(int index) {
            return (index > 0 && isLetterOrDigit(index - 1))
                && (index == mText.length() || !isLetterOrDigit(index));
        }

        private boolean isLetterOrDigit(int index) {
        private boolean isLetterOrDigit(int index) {
            if (index >= 0 && index < mText.length()) {
            if (index >= 0 && index < mText.length()) {
                final int codePoint = mText.codePointAt(index);
                final int codePoint = mText.codePointAt(index);
@@ -276,31 +275,19 @@ public final class AccessibilityIterators {
            if (offset >= textLength) {
            if (offset >= textLength) {
                return null;
                return null;
            }
            }
            int start = -1;
            int start = offset;
            if (offset < 0) {
                start = 0;
            } else {
                for (int i = offset + 1; i < textLength; i++) {
                    if (mText.charAt(i) == '\n') {
                        start = i;
                        break;
                    }
                }
            }
            if (start < 0) {
            if (start < 0) {
                return null;
                start = 0;
            }
            }
            while (start < textLength && mText.charAt(start) == '\n') {
            while (start < textLength && mText.charAt(start) == '\n'
                    && !isStartBoundary(start)) {
                start++;
                start++;
            }
            }
            int end = start;
            if (start >= textLength) {
            for (int i = end + 1; i < textLength; i++) {
                return null;
                end = i;
                if (mText.charAt(i) == '\n') {
                    break;
                }
            }
            }
            while (end < textLength && mText.charAt(end) == '\n') {
            int end = start + 1;
            while (end < textLength && !isEndBoundary(end)) {
                end++;
                end++;
            }
            }
            return getRange(start, end);
            return getRange(start, end);
@@ -315,38 +302,31 @@ public final class AccessibilityIterators {
            if (offset <= 0) {
            if (offset <= 0) {
                return null;
                return null;
            }
            }
            int end = -1;
            int end = offset;
            if (offset > mText.length()) {
            if (end > textLength) {
                end = mText.length();
                end = textLength;
            } else {
                if (offset > 0 && mText.charAt(offset - 1) == '\n') {
                    offset--;
                }
                for (int i = offset - 1; i >= 0; i--) {
                    if (i > 0 && mText.charAt(i - 1) == '\n') {
                        end = i;
                        break;
                    }
            }
            }
            while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) {
                end--;
            }
            }
            if (end <= 0) {
            if (end <= 0) {
                return null;
                return null;
            }
            }
            int start = end;
            int start = end - 1;
            while (start > 0 && mText.charAt(start - 1) == '\n') {
            while (start > 0 && !isStartBoundary(start)) {
                start--;
                start--;
            }
            }
            if (start == 0 && mText.charAt(start) == '\n') {
            return getRange(start, end);
                return null;
            }
            for (int i = start - 1; i >= 0; i--) {
                start = i;
                if (start > 0 && mText.charAt(i - 1) == '\n') {
                    break;
        }
        }

        private boolean isStartBoundary(int index) {
            return (mText.charAt(index) != '\n'
                && (index == 0 || mText.charAt(index - 1) == '\n'));
        }
        }
            start = Math.max(0, start);

            return getRange(start, end);
        private boolean isEndBoundary(int index) {
            return (index > 0 && mText.charAt(index - 1) != '\n'
                && (index == mText.length() || mText.charAt(index) == '\n'));
        }
        }
    }
    }
}
}
+23 −8
Original line number Original line Diff line number Diff line
@@ -1596,7 +1596,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
    /**
    /**
     * @hide
     * @hide
     */
     */
    private int mAccessibilityCursorPosition = -1;
    private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
    /**
    /**
     * The view's tag.
     * The view's tag.
@@ -2467,6 +2467,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
     */
     */
    public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
    public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
    /**
     * The undefined cursor position.
     */
    private static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1;
    /**
    /**
     * Indicates that the screen has changed state and is now off.
     * Indicates that the screen has changed state and is now off.
     *
     *
@@ -6202,7 +6207,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
            notifyAccessibilityStateChanged();
            notifyAccessibilityStateChanged();
            // Clear the text navigation state.
            // Clear the text navigation state.
            setAccessibilityCursorPosition(-1);
            setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
        }
        }
        // Clear the global reference of accessibility focus if this
        // Clear the global reference of accessibility focus if this
        // view or any of its descendants had accessibility focus.
        // view or any of its descendants had accessibility focus.
@@ -6252,6 +6257,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
    void clearAccessibilityFocusNoCallbacks() {
    void clearAccessibilityFocusNoCallbacks() {
        if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
        if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
            mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
            mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
            setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
            invalidate();
            invalidate();
        }
        }
    }
    }
@@ -6681,12 +6687,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
        final int current = getAccessibilityCursorPosition();
        final int current = getAccessibilityCursorPosition();
        final int[] range = iterator.following(current);
        final int[] range = iterator.following(current);
        if (range == null) {
        if (range == null) {
            setAccessibilityCursorPosition(-1);
            return false;
            return false;
        }
        }
        final int start = range[0];
        final int start = range[0];
        final int end = range[1];
        final int end = range[1];
        setAccessibilityCursorPosition(start);
        setAccessibilityCursorPosition(end);
        sendViewTextTraversedAtGranularityEvent(
        sendViewTextTraversedAtGranularityEvent(
                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                granularity, start, end);
                granularity, start, end);
@@ -6702,16 +6707,26 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
        if (iterator == null) {
        if (iterator == null) {
            return false;
            return false;
        }
        }
        final int selectionStart = getAccessibilityCursorPosition();
        int current = getAccessibilityCursorPosition();
        final int current = selectionStart >= 0 ? selectionStart : text.length() + 1;
        if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
            current = text.length();
        } 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--;
        }
        final int[] range = iterator.preceding(current);
        final int[] range = iterator.preceding(current);
        if (range == null) {
        if (range == null) {
            setAccessibilityCursorPosition(-1);
            return false;
            return false;
        }
        }
        final int start = range[0];
        final int start = range[0];
        final int end = range[1];
        final int end = range[1];
        // Always put the cursor after the character to ease edit.
        if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
            setAccessibilityCursorPosition(end);
            setAccessibilityCursorPosition(end);
        } else {
            setAccessibilityCursorPosition(start);
        }
        sendViewTextTraversedAtGranularityEvent(
        sendViewTextTraversedAtGranularityEvent(
                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
                granularity, start, end);
                granularity, start, end);
+25 −52
Original line number Original line Diff line number Diff line
@@ -56,16 +56,18 @@ final class AccessibilityIterators {
            if (offset >= mText.length()) {
            if (offset >= mText.length()) {
                return null;
                return null;
            }
            }
            int nextLine = -1;
            int nextLine;
            if (offset < 0) {
            if (offset < 0) {
                nextLine = mLayout.getLineForOffset(0);
                nextLine = mLayout.getLineForOffset(0);
            } else {
            } else {
                final int currentLine = mLayout.getLineForOffset(offset);
                final int currentLine = mLayout.getLineForOffset(offset);
                if (currentLine < mLayout.getLineCount() - 1) {
                if (getLineEdgeIndex(currentLine, DIRECTION_START) == offset) {
                    nextLine = currentLine;
                } else {
                    nextLine = currentLine + 1;
                    nextLine = currentLine + 1;
                }
                }
            }
            }
            if (nextLine < 0) {
            if (nextLine >= mLayout.getLineCount()) {
                return null;
                return null;
            }
            }
            final int start = getLineEdgeIndex(nextLine, DIRECTION_START);
            final int start = getLineEdgeIndex(nextLine, DIRECTION_START);
@@ -82,12 +84,14 @@ final class AccessibilityIterators {
            if (offset <= 0) {
            if (offset <= 0) {
                return null;
                return null;
            }
            }
            int previousLine = -1;
            int previousLine;
            if (offset > mText.length()) {
            if (offset > mText.length()) {
                previousLine = mLayout.getLineForOffset(mText.length());
                previousLine = mLayout.getLineForOffset(mText.length());
            } else {
            } else {
                final int currentLine = mLayout.getLineForOffset(offset - 1);
                final int currentLine = mLayout.getLineForOffset(offset);
                if (currentLine > 0) {
                if (getLineEdgeIndex(currentLine, DIRECTION_END) + 1 == offset) {
                    previousLine = currentLine;
                } else {
                    previousLine = currentLine - 1;
                    previousLine = currentLine - 1;
                }
                }
            }
            }
@@ -141,29 +145,18 @@ final class AccessibilityIterators {
                return null;
                return null;
            }
            }


            final int currentLine = mLayout.getLineForOffset(offset);
            final int start = Math.max(0, offset);

            final int currentLine = mLayout.getLineForOffset(start);
            final int currentLineTop = mLayout.getLineTop(currentLine);
            final int currentLineTop = mLayout.getLineTop(currentLine);
            final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
            final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
                    - mView.getTotalPaddingBottom();
                    - mView.getTotalPaddingBottom();

            final int nextPageStartLine;
            final int nextPageEndLine;
            if (offset < 0) {
                nextPageStartLine = currentLine;
                final int nextPageEndY = currentLineTop + pageHeight;
                nextPageEndLine = mLayout.getLineForVertical(nextPageEndY);
            } else {
            final int nextPageStartY = currentLineTop + pageHeight;
            final int nextPageStartY = currentLineTop + pageHeight;
                nextPageStartLine = mLayout.getLineForVertical(nextPageStartY) + 1;
            final int lastLineTop = mLayout.getLineTop(mLayout.getLineCount() - 1);
                if (mLayout.getLineTop(nextPageStartLine) <= nextPageStartY) {
            final int currentPageEndLine = (nextPageStartY < lastLineTop)
                    return null;
                    ? mLayout.getLineForVertical(nextPageStartY) - 1 : mLayout.getLineCount() - 1;
                }
                final int nextPageEndY = nextPageStartY + pageHeight;
                nextPageEndLine = mLayout.getLineForVertical(nextPageEndY);
            }


            final int start = getLineEdgeIndex(nextPageStartLine, DIRECTION_START);
            final int end = getLineEdgeIndex(currentPageEndLine, DIRECTION_END) + 1;
            final int end = getLineEdgeIndex(nextPageEndLine, DIRECTION_END) + 1;


            return getRange(start, end);
            return getRange(start, end);
        }
        }
@@ -181,37 +174,17 @@ final class AccessibilityIterators {
                return null;
                return null;
            }
            }


            final int currentLine = mLayout.getLineForOffset(offset);
            final int end = Math.min(mText.length(), offset);

            final int currentLine = mLayout.getLineForOffset(end);
            final int currentLineTop = mLayout.getLineTop(currentLine);
            final int currentLineTop = mLayout.getLineTop(currentLine);
            final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
            final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
                    - mView.getTotalPaddingBottom();
                    - mView.getTotalPaddingBottom();
            final int previousPageEndY = currentLineTop - pageHeight;
            final int currentPageStartLine = (previousPageEndY > 0) ?
                     mLayout.getLineForVertical(previousPageEndY) + 1 : 0;


            final int previousPageStartLine;
            final int start = getLineEdgeIndex(currentPageStartLine, DIRECTION_START);
            final int previousPageEndLine;
            if (offset > mText.length()) {
                final int prevousPageStartY = mLayout.getHeight() - pageHeight;
                if (prevousPageStartY < 0) {
                    return null;
                }
                previousPageStartLine = mLayout.getLineForVertical(prevousPageStartY);
                previousPageEndLine = mLayout.getLineCount() - 1;
            } else {
                final int prevousPageStartY;
                if (offset == mText.length()) {
                    prevousPageStartY = mLayout.getHeight() - 2 * pageHeight;
                } else {
                    prevousPageStartY = currentLineTop - 2 * pageHeight;
                }
                if (prevousPageStartY < 0) {
                    return null;
                }
                previousPageStartLine = mLayout.getLineForVertical(prevousPageStartY);
                final int previousPageEndY = prevousPageStartY + pageHeight;
                previousPageEndLine = mLayout.getLineForVertical(previousPageEndY) - 1;
            }

            final int start = getLineEdgeIndex(previousPageStartLine, DIRECTION_START);
            final int end = getLineEdgeIndex(previousPageEndLine, DIRECTION_END) + 1;


            return getRange(start, end);
            return getRange(start, end);
        }
        }
+6 −4
Original line number Original line Diff line number Diff line
@@ -8376,11 +8376,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    @Override
    @Override
    public int getAccessibilityCursorPosition() {
    public int getAccessibilityCursorPosition() {
        if (TextUtils.isEmpty(getContentDescription())) {
        if (TextUtils.isEmpty(getContentDescription())) {
            return getSelectionEnd();
            final int selectionEnd = getSelectionEnd();
        } else {
            if (selectionEnd >= 0) {
            return super.getAccessibilityCursorPosition();
                return selectionEnd;
            }
            }
        }
        }
        return super.getAccessibilityCursorPosition();
    }


    /**
    /**
     * @hide
     * @hide
@@ -8391,7 +8393,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            return;
            return;
        }
        }
        if (TextUtils.isEmpty(getContentDescription())) {
        if (TextUtils.isEmpty(getContentDescription())) {
            if (index >= 0) {
            if (index >= 0 && index <= mText.length()) {
                Selection.setSelection((Spannable) mText, index);
                Selection.setSelection((Spannable) mText, index);
            } else {
            } else {
                Selection.removeSelection((Spannable) mText);
                Selection.removeSelection((Spannable) mText);