Loading core/java/android/widget/SpellChecker.java +20 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.text.Editable; import android.text.Selection; import android.text.Spanned; import android.text.SpannedString; import android.text.method.WordIterator; import android.text.style.SpellCheckSpan; import android.text.style.SuggestionSpan; Loading Loading @@ -416,7 +417,15 @@ public class SpellChecker implements SpellCheckerSessionListener { } if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart && end > start) { removeErrorSuggestionSpan(editable, start, end, RemoveReason.OBSOLETE); boolean visibleToAccessibility = mTextView.isVisibleToAccessibility(); CharSequence beforeText = visibleToAccessibility ? new SpannedString(editable) : null; boolean spanRemoved = removeErrorSuggestionSpan( editable, start, end, RemoveReason.OBSOLETE); if (visibleToAccessibility && spanRemoved) { mTextView.sendAccessibilityEventTypeViewTextChanged( beforeText, start, end); } } } return spellCheckSpan; Loading @@ -437,8 +446,9 @@ public class SpellChecker implements SpellCheckerSessionListener { OBSOLETE, } private static void removeErrorSuggestionSpan( private static boolean removeErrorSuggestionSpan( Editable editable, int start, int end, RemoveReason reason) { boolean spanRemoved = false; SuggestionSpan[] spans = editable.getSpans(start, end, SuggestionSpan.class); for (SuggestionSpan span : spans) { if (editable.getSpanStart(span) == start Loading @@ -450,8 +460,10 @@ public class SpellChecker implements SpellCheckerSessionListener { + editable.subSequence(start, end) + ", reason: " + reason); } editable.removeSpan(span); spanRemoved = true; } } return spanRemoved; } @Override Loading Loading @@ -568,8 +580,13 @@ public class SpellChecker implements SpellCheckerSessionListener { } SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions, flags); removeErrorSuggestionSpan(editable, start, end, RemoveReason.REPLACE); boolean spanRemoved = removeErrorSuggestionSpan(editable, start, end, RemoveReason.REPLACE); boolean sendAccessibilityEvent = !spanRemoved && mTextView.isVisibleToAccessibility(); CharSequence beforeText = sendAccessibilityEvent ? new SpannedString(editable) : null; editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); if (sendAccessibilityEvent) { mTextView.sendAccessibilityEventTypeViewTextChanged(beforeText, start, end); } mTextView.invalidateRegion(start, end, false /* No cursor involved */); } Loading core/java/android/widget/TextView.java +17 −51 Original line number Diff line number Diff line Loading @@ -12501,6 +12501,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return TextUtils.trimToParcelableSize(mTransformed); } boolean isVisibleToAccessibility() { return AccessibilityManager.getInstance(mContext).isEnabled() && (isFocused() || (isSelected() && isShown())); } void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, int removedCount, int addedCount) { AccessibilityEvent event = Loading @@ -12512,6 +12517,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendAccessibilityEventUnchecked(event); } void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, int toIndex) { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); event.setFromIndex(fromIndex); event.setToIndex(toIndex); event.setBeforeText(beforeText); sendAccessibilityEventUnchecked(event); } private InputMethodManager getInputMethodManager() { return getContext().getSystemService(InputMethodManager.class); } Loading Loading @@ -13826,10 +13841,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } TextView.this.handleTextChanged(buffer, start, before, after); if (AccessibilityManager.getInstance(mContext).isEnabled() && (isFocused() || (isSelected() && isShown()))) { if (isVisibleToAccessibility()) { sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); mBeforeText = TextUtils.stringOrSpannedString(mTransformed); mBeforeText = null; } } Loading Loading @@ -13857,54 +13871,6 @@ 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) { Loading
core/java/android/widget/SpellChecker.java +20 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.text.Editable; import android.text.Selection; import android.text.Spanned; import android.text.SpannedString; import android.text.method.WordIterator; import android.text.style.SpellCheckSpan; import android.text.style.SuggestionSpan; Loading Loading @@ -416,7 +417,15 @@ public class SpellChecker implements SpellCheckerSessionListener { } if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart && end > start) { removeErrorSuggestionSpan(editable, start, end, RemoveReason.OBSOLETE); boolean visibleToAccessibility = mTextView.isVisibleToAccessibility(); CharSequence beforeText = visibleToAccessibility ? new SpannedString(editable) : null; boolean spanRemoved = removeErrorSuggestionSpan( editable, start, end, RemoveReason.OBSOLETE); if (visibleToAccessibility && spanRemoved) { mTextView.sendAccessibilityEventTypeViewTextChanged( beforeText, start, end); } } } return spellCheckSpan; Loading @@ -437,8 +446,9 @@ public class SpellChecker implements SpellCheckerSessionListener { OBSOLETE, } private static void removeErrorSuggestionSpan( private static boolean removeErrorSuggestionSpan( Editable editable, int start, int end, RemoveReason reason) { boolean spanRemoved = false; SuggestionSpan[] spans = editable.getSpans(start, end, SuggestionSpan.class); for (SuggestionSpan span : spans) { if (editable.getSpanStart(span) == start Loading @@ -450,8 +460,10 @@ public class SpellChecker implements SpellCheckerSessionListener { + editable.subSequence(start, end) + ", reason: " + reason); } editable.removeSpan(span); spanRemoved = true; } } return spanRemoved; } @Override Loading Loading @@ -568,8 +580,13 @@ public class SpellChecker implements SpellCheckerSessionListener { } SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions, flags); removeErrorSuggestionSpan(editable, start, end, RemoveReason.REPLACE); boolean spanRemoved = removeErrorSuggestionSpan(editable, start, end, RemoveReason.REPLACE); boolean sendAccessibilityEvent = !spanRemoved && mTextView.isVisibleToAccessibility(); CharSequence beforeText = sendAccessibilityEvent ? new SpannedString(editable) : null; editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); if (sendAccessibilityEvent) { mTextView.sendAccessibilityEventTypeViewTextChanged(beforeText, start, end); } mTextView.invalidateRegion(start, end, false /* No cursor involved */); } Loading
core/java/android/widget/TextView.java +17 −51 Original line number Diff line number Diff line Loading @@ -12501,6 +12501,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return TextUtils.trimToParcelableSize(mTransformed); } boolean isVisibleToAccessibility() { return AccessibilityManager.getInstance(mContext).isEnabled() && (isFocused() || (isSelected() && isShown())); } void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, int removedCount, int addedCount) { AccessibilityEvent event = Loading @@ -12512,6 +12517,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendAccessibilityEventUnchecked(event); } void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, int toIndex) { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); event.setFromIndex(fromIndex); event.setToIndex(toIndex); event.setBeforeText(beforeText); sendAccessibilityEventUnchecked(event); } private InputMethodManager getInputMethodManager() { return getContext().getSystemService(InputMethodManager.class); } Loading Loading @@ -13826,10 +13841,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } TextView.this.handleTextChanged(buffer, start, before, after); if (AccessibilityManager.getInstance(mContext).isEnabled() && (isFocused() || (isSelected() && isShown()))) { if (isVisibleToAccessibility()) { sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); mBeforeText = TextUtils.stringOrSpannedString(mTransformed); mBeforeText = null; } } Loading Loading @@ -13857,54 +13871,6 @@ 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) {