From fb12492de73fdb4e932fd5f10a1691bd746123c7 Mon Sep 17 00:00:00 2001 From: Ahaan Ugale Date: Mon, 14 Jun 2021 23:21:47 -0700 Subject: [PATCH 1/2] ContentCapture: Notify when Composing region changes. Sending the notification from the InputConnection layer reduces the number of events that are handled (merged) by ContentCapture. We are also able to handle composing span being removed; at the TextView layer, we cannot do that as we don't know if the composing span will be added back, which happens on typing). Bug: 184311217 Test: manual - append chars, delete chars, replace text, move cursor to composing text, move cursor to remove composing span, drag cursor Test: atest android.contentcaptureservice.cts.LoginActivityTest Change-Id: I0e9d153cf7ba2734f38cb1b6044eb7b670fb34ad --- .../view/inputmethod/BaseInputConnection.java | 13 +++++++++++++ core/java/android/widget/TextView.java | 12 ++++++++++-- .../internal/widget/EditableInputConnection.java | 6 ++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index bdd12063e6cf3..c4540b0d8726f 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -162,6 +162,17 @@ public class BaseInputConnection implements InputConnection { return false; } + /** + * Called after only the composing region is modified (so it isn't called if the text also + * changes). + *

+ * Default implementation does nothing. + * + * @hide + */ + public void endComposingRegionEditInternal() { + } + /** * Default implementation calls {@link #finishComposingText()} and * {@code setImeConsumesInput(false)}. @@ -468,6 +479,7 @@ public class BaseInputConnection implements InputConnection { // Note: sendCurrentText does nothing unless mFallbackMode is set sendCurrentText(); endBatchEdit(); + endComposingRegionEditInternal(); } return true; } @@ -734,6 +746,7 @@ public class BaseInputConnection implements InputConnection { // Note: sendCurrentText does nothing unless mFallbackMode is set sendCurrentText(); endBatchEdit(); + endComposingRegionEditInternal(); } return true; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3c4fd5e935804..cd560d75d913a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10832,11 +10832,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + notifyContentCaptureTextChanged(); + } + + /** + * Notifies the ContentCapture service that the text of the view has changed (only if + * ContentCapture has been notified of this view's existence already). + * + * @hide + */ + public void notifyContentCaptureTextChanged() { // TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead // of using isLaidout(), so it's not called in cases where it's laid out but a // notifyAppeared was not sent. - - // ContentCapture if (isLaidOut() && isImportantForContentCapture() && getNotifiedContentCaptureAppeared()) { final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class); if (cm != null && cm.isContentCaptureEnabled()) { diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 3d054a5e773c4..02ffe8c5268ef 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -98,6 +98,12 @@ public class EditableInputConnection extends BaseInputConnection return false; } + @Override + public void endComposingRegionEditInternal() { + // The ContentCapture service is interested in Composing-state changes. + mTextView.notifyContentCaptureTextChanged(); + } + @Override public void closeConnection() { super.closeConnection(); From 01315b10891d5a5d3a6b8af217538b793715cc90 Mon Sep 17 00:00:00 2001 From: Ahaan Ugale Date: Tue, 15 Jun 2021 21:56:53 -0700 Subject: [PATCH 2/2] ContentCapture: Don't ignore events with changed composing/selection span Currently, if the view has a TEXT_CHANGED event in the buffer with the same text, the new event is ignored, even if the Composing or Selection span is changed. Bug: 184311217 Test: manual - move cursor quickly and check flushed event Test: atest android.contentcaptureservice.cts.LoginActivityTest Change-Id: I4de28576b138225f4e64ed93bcb0b9dd3ed0095c --- .../android/view/contentcapture/ContentCaptureEvent.java | 9 +++++++++ .../view/contentcapture/MainContentCaptureSession.java | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 1b1dc4a709cad..d982dc90e0ea7 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -285,6 +285,15 @@ public final class ContentCaptureEvent implements Parcelable { return this; } + boolean hasSameComposingSpan(@NonNull ContentCaptureEvent other) { + return mComposingStart == other.mComposingStart && mComposingEnd == other.mComposingEnd; + } + + boolean hasSameSelectionSpan(@NonNull ContentCaptureEvent other) { + return mSelectionStartIndex == other.mSelectionStartIndex + && mSelectionEndIndex == other.mSelectionEndIndex; + } + private int getComposingStart() { return mComposingStart; } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index d8ac779ddc275..d0a2e771e097c 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -362,7 +362,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession { final CharSequence lastText = lastEvent.getText(); final boolean bothNonEmpty = !TextUtils.isEmpty(lastText) && !TextUtils.isEmpty(text); - boolean equalContent = TextUtils.equals(lastText, text); + boolean equalContent = + TextUtils.equals(lastText, text) + && lastEvent.hasSameComposingSpan(event) + && lastEvent.hasSameSelectionSpan(event); if (equalContent) { addEvent = false; } else if (bothNonEmpty) {