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

Commit f290a87c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Make some NoCopySpans can pass to ContentCaptureService" into sc-dev am: 7063bae0

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14891918

Change-Id: I5dc35c9b31952db6f44e21cb9166e83630aa8644
parents c7081f37 7063bae0
Loading
Loading
Loading
Loading
+106 −10
Original line number Original line Diff line number Diff line
@@ -25,8 +25,12 @@ import android.annotation.SystemApi;
import android.graphics.Insets;
import android.graphics.Insets;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableString;
import android.util.Log;
import android.util.Log;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillId;
import android.view.inputmethod.BaseInputConnection;


import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;


@@ -132,6 +136,9 @@ public final class ContentCaptureEvent implements Parcelable {
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface EventType{}
    public @interface EventType{}


    /** @hide */
    public static final int MAX_INVALID_VALUE = -1;

    private final int mSessionId;
    private final int mSessionId;
    private final int mType;
    private final int mType;
    private final long mEventTime;
    private final long mEventTime;
@@ -143,6 +150,11 @@ public final class ContentCaptureEvent implements Parcelable {
    private @Nullable ContentCaptureContext mClientContext;
    private @Nullable ContentCaptureContext mClientContext;
    private @Nullable Insets mInsets;
    private @Nullable Insets mInsets;


    private int mComposingStart = MAX_INVALID_VALUE;
    private int mComposingEnd = MAX_INVALID_VALUE;
    private int mSelectionStartIndex = MAX_INVALID_VALUE;
    private int mSelectionEndIndex = MAX_INVALID_VALUE;

    /** Only used in the main Content Capture session, no need to parcel */
    /** Only used in the main Content Capture session, no need to parcel */
    private boolean mTextHasComposingSpan;
    private boolean mTextHasComposingSpan;


@@ -246,19 +258,75 @@ public final class ContentCaptureEvent implements Parcelable {


    /** @hide */
    /** @hide */
    @NonNull
    @NonNull
    public ContentCaptureEvent setText(@Nullable CharSequence text, boolean hasComposingSpan) {
    public ContentCaptureEvent setText(@Nullable CharSequence text) {
        mText = text;
        mText = text;
        mTextHasComposingSpan = hasComposingSpan;
        return this;
        return this;
    }
    }


    /**
    /** @hide */
     * The value is not parcelled, become false after parcelled.
    @NonNull
     * @hide
    public ContentCaptureEvent setComposingIndex(int start, int end) {
     */
        mComposingStart = start;
        mComposingEnd = end;
        return this;
    }

    /** @hide */
    @NonNull
    public boolean hasComposingSpan() {
        return mComposingStart > MAX_INVALID_VALUE;
    }

    /** @hide */
    @NonNull
    @NonNull
    public boolean getTextHasComposingSpan() {
    public ContentCaptureEvent setSelectionIndex(int start, int end) {
        return mTextHasComposingSpan;
        mSelectionStartIndex = start;
        mSelectionEndIndex = end;
        return this;
    }

    private int getComposingStart() {
        return mComposingStart;
    }

    private int getComposingEnd() {
        return mComposingEnd;
    }

    private int getSelectionStart() {
        return mSelectionStartIndex;
    }

    private int getSelectionEnd() {
        return mSelectionEndIndex;
    }

    private void restoreComposingSpan() {
        if (mComposingStart <= MAX_INVALID_VALUE
                || mComposingEnd <= MAX_INVALID_VALUE) {
            return;
        }
        if (mText instanceof Spannable) {
            BaseInputConnection.setComposingSpans((Spannable) mText, mComposingStart,
                    mComposingEnd);
        } else {
            Log.w(TAG, "Text is not a Spannable.");
        }
    }

    private void restoreSelectionSpans() {
        if (mSelectionStartIndex <= MAX_INVALID_VALUE
                || mSelectionEndIndex <= MAX_INVALID_VALUE) {
            return;
        }

        if (mText instanceof SpannableString) {
            SpannableString ss = (SpannableString) mText;
            ss.setSpan(Selection.SELECTION_START, mSelectionStartIndex, mSelectionStartIndex, 0);
            ss.setSpan(Selection.SELECTION_END, mSelectionEndIndex, mSelectionEndIndex, 0);
        } else {
            Log.w(TAG, "Text is not a SpannableString.");
        }
    }
    }


    /** @hide */
    /** @hide */
@@ -374,7 +442,9 @@ public final class ContentCaptureEvent implements Parcelable {
            throw new IllegalArgumentException("mergeEvent(): got "
            throw new IllegalArgumentException("mergeEvent(): got "
                    + "TYPE_VIEW_DISAPPEARED event with neither id or ids: " + event);
                    + "TYPE_VIEW_DISAPPEARED event with neither id or ids: " + event);
        } else if (eventType == TYPE_VIEW_TEXT_CHANGED) {
        } else if (eventType == TYPE_VIEW_TEXT_CHANGED) {
            setText(event.getText(), event.getTextHasComposingSpan());
            setText(event.getText());
            setComposingIndex(event.getComposingStart(), event.getComposingEnd());
            setSelectionIndex(event.getSelectionStart(), event.getSelectionEnd());
        } else {
        } else {
            Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType)
            Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType)
                    + ") does not support this event type.");
                    + ") does not support this event type.");
@@ -409,6 +479,14 @@ public final class ContentCaptureEvent implements Parcelable {
        if (mInsets != null) {
        if (mInsets != null) {
            pw.print(", insets="); pw.println(mInsets);
            pw.print(", insets="); pw.println(mInsets);
        }
        }
        if (mComposingStart > MAX_INVALID_VALUE) {
            pw.print(", composing("); pw.print(mComposingStart);
            pw.print(", "); pw.print(mComposingEnd); pw.print(")");
        }
        if (mSelectionStartIndex > MAX_INVALID_VALUE) {
            pw.print(", selection("); pw.print(mSelectionStartIndex);
            pw.print(", "); pw.print(mSelectionEndIndex); pw.print(")");
        }
    }
    }


    @NonNull
    @NonNull
@@ -443,6 +521,12 @@ public final class ContentCaptureEvent implements Parcelable {
        if (mInsets != null) {
        if (mInsets != null) {
            string.append(", insets=").append(mInsets);
            string.append(", insets=").append(mInsets);
        }
        }
        if (mComposingStart > MAX_INVALID_VALUE) {
            string.append(", hasComposing");
        }
        if (mSelectionStartIndex > MAX_INVALID_VALUE) {
            string.append(", hasSelection");
        }
        return string.append(']').toString();
        return string.append(']').toString();
    }
    }


@@ -469,6 +553,12 @@ public final class ContentCaptureEvent implements Parcelable {
        if (mType == TYPE_VIEW_INSETS_CHANGED) {
        if (mType == TYPE_VIEW_INSETS_CHANGED) {
            parcel.writeParcelable(mInsets, flags);
            parcel.writeParcelable(mInsets, flags);
        }
        }
        if (mType == TYPE_VIEW_TEXT_CHANGED) {
            parcel.writeInt(mComposingStart);
            parcel.writeInt(mComposingEnd);
            parcel.writeInt(mSelectionStartIndex);
            parcel.writeInt(mSelectionEndIndex);
        }
    }
    }


    public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureEvent> CREATOR =
    public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -493,7 +583,7 @@ public final class ContentCaptureEvent implements Parcelable {
            if (node != null) {
            if (node != null) {
                event.setViewNode(node);
                event.setViewNode(node);
            }
            }
            event.setText(parcel.readCharSequence(), false);
            event.setText(parcel.readCharSequence());
            if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
            if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
                event.setParentSessionId(parcel.readInt());
                event.setParentSessionId(parcel.readInt());
            }
            }
@@ -503,6 +593,12 @@ public final class ContentCaptureEvent implements Parcelable {
            if (type == TYPE_VIEW_INSETS_CHANGED) {
            if (type == TYPE_VIEW_INSETS_CHANGED) {
                event.setInsets(parcel.readParcelable(null));
                event.setInsets(parcel.readParcelable(null));
            }
            }
            if (type == TYPE_VIEW_TEXT_CHANGED) {
                event.setComposingIndex(parcel.readInt(), parcel.readInt());
                event.restoreComposingSpan();
                event.setSelectionIndex(parcel.readInt(), parcel.readInt());
                event.restoreSelectionSpans();
            }
            return event;
            return event;
        }
        }


+20 −7
Original line number Original line Diff line number Diff line
@@ -43,6 +43,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.RemoteException;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.Spanned;
@@ -347,8 +348,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
            //    2.2 last event doesn't have composing span: add.
            //    2.2 last event doesn't have composing span: add.
            // Otherwise, merge.
            // Otherwise, merge.
            final CharSequence text = event.getText();
            final CharSequence text = event.getText();
            final boolean textHasComposingSpan = event.getTextHasComposingSpan();
            final boolean hasComposingSpan = event.hasComposingSpan();
            if (textHasComposingSpan) {
            if (hasComposingSpan) {
                ContentCaptureEvent lastEvent = null;
                ContentCaptureEvent lastEvent = null;
                for (int index = mEvents.size() - 1; index >= 0; index--) {
                for (int index = mEvents.size() - 1; index >= 0; index--) {
                    final ContentCaptureEvent tmpEvent = mEvents.get(index);
                    final ContentCaptureEvent tmpEvent = mEvents.get(index);
@@ -357,7 +358,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
                        break;
                        break;
                    }
                    }
                }
                }
                if (lastEvent != null && lastEvent.getTextHasComposingSpan()) {
                if (lastEvent != null && lastEvent.hasComposingSpan()) {
                    final CharSequence lastText = lastEvent.getText();
                    final CharSequence lastText = lastEvent.getText();
                    final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
                    final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
                            && !TextUtils.isEmpty(text);
                            && !TextUtils.isEmpty(text);
@@ -705,12 +706,24 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
        // a copy of its content so that its value will not be changed by subsequent updates
        // a copy of its content so that its value will not be changed by subsequent updates
        // in the TextView.
        // in the TextView.
        final CharSequence eventText = stringOrSpannedStringWithoutNoCopySpans(text);
        final CharSequence eventText = stringOrSpannedStringWithoutNoCopySpans(text);
        final boolean textHasComposingSpan =

                text instanceof Spannable && BaseInputConnection.getComposingSpanStart(
        final int composingStart;
                        (Spannable) text) >= 0;
        final int composingEnd;
        if (text instanceof Spannable) {
            composingStart = BaseInputConnection.getComposingSpanStart((Spannable) text);
            composingEnd = BaseInputConnection.getComposingSpanEnd((Spannable) text);
        } else {
            composingStart = ContentCaptureEvent.MAX_INVALID_VALUE;
            composingEnd = ContentCaptureEvent.MAX_INVALID_VALUE;
        }

        final int startIndex = Selection.getSelectionStart(text);
        final int endIndex = Selection.getSelectionEnd(text);
        mHandler.post(() -> sendEvent(
        mHandler.post(() -> sendEvent(
                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
                        .setAutofillId(id).setText(eventText, textHasComposingSpan)));
                        .setAutofillId(id).setText(eventText)
                        .setComposingIndex(composingStart, composingEnd)
                        .setSelectionIndex(startIndex, endIndex)));
    }
    }


    private CharSequence stringOrSpannedStringWithoutNoCopySpans(CharSequence source) {
    private CharSequence stringOrSpannedStringWithoutNoCopySpans(CharSequence source) {
+8 −8
Original line number Original line Diff line number Diff line
@@ -236,13 +236,13 @@ public class ContentCaptureEventTest {
    @Test
    @Test
    public void testMergeEvent_typeViewTextChanged() {
    public void testMergeEvent_typeViewTextChanged() {
        final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_TEXT_CHANGED)
        final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_TEXT_CHANGED)
                .setText("test", false);
                .setText("test");
        final ContentCaptureEvent event2 = new ContentCaptureEvent(43, TYPE_VIEW_TEXT_CHANGED)
        final ContentCaptureEvent event2 = new ContentCaptureEvent(43, TYPE_VIEW_TEXT_CHANGED)
                .setText("empty", true);
                .setText("composing").setComposingIndex(0, 1);


        event.mergeEvent(event2);
        event.mergeEvent(event2);
        assertThat(event.getText()).isEqualTo(event2.getText());
        assertThat(event.getText()).isEqualTo(event2.getText());
        assertThat(event.getTextHasComposingSpan()).isEqualTo(event2.getTextHasComposingSpan());
        assertThat(event.hasComposingSpan()).isEqualTo(event2.hasComposingSpan());
    }
    }


    @Test
    @Test
@@ -283,18 +283,18 @@ public class ContentCaptureEventTest {
    @Test
    @Test
    public void testMergeEvent_differentEventTypes() {
    public void testMergeEvent_differentEventTypes() {
        final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_DISAPPEARED)
        final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_DISAPPEARED)
                .setText("test", false).setAutofillId(new AutofillId(1));
                .setText("test").setAutofillId(new AutofillId(1));
        final ContentCaptureEvent event2 = new ContentCaptureEvent(17, TYPE_VIEW_TEXT_CHANGED)
        final ContentCaptureEvent event2 = new ContentCaptureEvent(17, TYPE_VIEW_TEXT_CHANGED)
                .setText("empty", true).setAutofillId(new AutofillId(2));
                .setText("composing").setAutofillId(new AutofillId(2)).setComposingIndex(0, 1);


        event.mergeEvent(event2);
        event.mergeEvent(event2);
        assertThat(event.getText()).isEqualTo("test");
        assertThat(event.getText()).isEqualTo("test");
        assertThat(event.getTextHasComposingSpan()).isFalse();
        assertThat(event.hasComposingSpan()).isFalse();
        assertThat(event.getId()).isEqualTo(new AutofillId(1));
        assertThat(event.getId()).isEqualTo(new AutofillId(1));


        event2.mergeEvent(event);
        event2.mergeEvent(event);
        assertThat(event2.getText()).isEqualTo("empty");
        assertThat(event2.getText()).isEqualTo("composing");
        assertThat(event2.getTextHasComposingSpan()).isTrue();
        assertThat(event2.hasComposingSpan()).isTrue();
        assertThat(event2.getId()).isEqualTo(new AutofillId(2));
        assertThat(event2.getId()).isEqualTo(new AutofillId(2));
    }
    }