From bc3dd0b7fa39286a2ff3455cc873acb518f9432f Mon Sep 17 00:00:00 2001 From: yingleiw Date: Fri, 20 Aug 2021 15:51:02 -0700 Subject: [PATCH] Send TYPE_VIEW_TEXT_CHANGED a11y event when SuggestionSpan is added When user types space after a word, we receive a text change event, but the suggestion span is not added to the text yet. The spell checker adds the span after the text change event is sent, but currently TextView doesn't send any notification. This change sends a TYPE_VIEW_TEXT_CHANGED event with the from and to index of the span, and beforeText which doesn't have the span and afterText which has the span. Bug: b/143378480 Test: tested with talkback. Change-Id: If16c2c578be5dcae20a9fd7014ff6fba2d487670 --- core/java/android/widget/TextView.java | 52 +++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 69a5e39047159..3eaf852b3ac3c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -13822,9 +13822,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextView.this.handleTextChanged(buffer, start, before, after); if (AccessibilityManager.getInstance(mContext).isEnabled() - && (isFocused() || isSelected() && isShown())) { + && (isFocused() || (isSelected() && isShown()))) { sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); - mBeforeText = null; + mBeforeText = TextUtils.stringOrSpannedString(mTransformed); } } @@ -13852,6 +13852,54 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Log.v(LOG_TAG, "onSpanAdded s=" + s + " e=" + e + " what=" + what + ": " + buf); } TextView.this.spanChange(buf, what, -1, s, -1, e); + // Note we don't update mBeforeText here. We look for SuggestionSpans added after the + // text content changes. + if (AccessibilityManager.getInstance(mContext).isEnabled() + && (isFocused() || (isSelected() && isShown())) + && (what instanceof SuggestionSpan)) { + // When the user types a new word, and SuggestionSpans on the existing words will be + // removed and added again. We don't need to send out events for existing + // SuggestionSpans. Multiple spans can be placed on the range. + if (mBeforeText instanceof SpannedString) { + final SpannedString beforeSpannedString = (SpannedString) mBeforeText; + if ((beforeSpannedString.getSpanStart(what) == s) + && (beforeSpannedString.getSpanEnd(what) == e)) { + // Exactly same span is found. + return; + } + // Suggestion span couldn't be found. Try to find a suggestion span that has the + // same contents. + SuggestionSpan[] suggestionSpans = beforeSpannedString.getSpans(s, e, + SuggestionSpan.class); + for (final SuggestionSpan suggestionSpan : suggestionSpans) { + final int start = beforeSpannedString.getSpanStart(suggestionSpan); + if (start != s) { + continue; + } + final int end = beforeSpannedString.getSpanEnd(suggestionSpan); + if (end != e) { + continue; + } + if (equalSuggestionSpan(suggestionSpan, (SuggestionSpan) what)) { + return; + } + } + } + AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); + event.setFromIndex(s); + event.setToIndex(e); + event.setBeforeText(mBeforeText); + sendAccessibilityEventUnchecked(event); + } + } + + private boolean equalSuggestionSpan(SuggestionSpan span1, SuggestionSpan span2) { + // We compare flags because flags will determine the underline color. + return Arrays.equals(span1.getSuggestions(), span2.getSuggestions()) + && Objects.equals(span1.getLocaleObject(), span2.getLocaleObject()) + && span1.getLocale().equals(span2.getLocale()) + && (span1.getFlags() == span2.getFlags()); } public void onSpanRemoved(Spannable buf, Object what, int s, int e) {