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

Commit 7fbcbf27 authored by Taran Singh's avatar Taran Singh
Browse files

Scribe in IMF: Introduce filters in CursoAnchorInfo API 7/N

New filter modes for InputConnection#requestCursorUpdates(mode) to allow
partial data in cursor/anchor updates.
Selective data helps with speedy CursorAnchorInfo updates.

Test: atest InputMethodServiceTest
Bug: 203086136

Change-Id: Ibba06e9a2533ca91c5cc215dfb18d21c9a74fb73
parent 342509f0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -52414,6 +52414,9 @@ package android.view.inputmethod {
    method public default boolean setImeConsumesInput(boolean);
    method public boolean setSelection(int, int);
    method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
    field public static final int CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS = 8; // 0x8
    field public static final int CURSOR_UPDATE_FILTER_EDITOR_BOUNDS = 4; // 0x4
    field public static final int CURSOR_UPDATE_FILTER_INSERTION_MARKER = 16; // 0x10
    field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
    field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
    field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1
+62 −3
Original line number Diff line number Diff line
@@ -972,6 +972,13 @@ public interface InputConnection {
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
     * once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
     * used together with {@link #CURSOR_UPDATE_MONITOR}.
     * <p>
     * Note by default all of {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
     * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS} and
     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} are included but specifying them can
     * filter-out others.
     * It can be CPU intensive to include all, filtering specific info is recommended.
     * </p>
     */
    int CURSOR_UPDATE_IMMEDIATE = 1 << 0;

@@ -983,17 +990,69 @@ public interface InputConnection {
     * <p>
     * This flag can be used together with {@link #CURSOR_UPDATE_IMMEDIATE}.
     * </p>
     * <p>
     * Note by default all of {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
     * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS} and
     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} are included but specifying them can
     * filter-out others.
     * It can be CPU intensive to include all, filtering specific info is recommended.
     * </p>
     */
    int CURSOR_UPDATE_MONITOR = 1 << 1;

    /**
     * The editor is requested to call
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
     * with new {@link EditorBoundsInfo} whenever cursor/anchor position is changed. To disable
     * monitoring, call {@link InputConnection#requestCursorUpdates(int)} again with this flag off.
     * <p>
     * This flag can be used together with filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} and update flags
     * {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
     * </p>
     */
    int CURSOR_UPDATE_FILTER_EDITOR_BOUNDS = 1 << 2;

    /**
     * The editor is requested to call
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
     * with new character bounds {@link CursorAnchorInfo#getCharacterBounds(int)} whenever
     * cursor/anchor position is changed. To disable
     * monitoring, call {@link InputConnection#requestCursorUpdates(int)} again with this flag off.
     * <p>
     * This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} and update flags
     * {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
     * </p>
     */
    int CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS = 1 << 3;

    /**
     * The editor is requested to call
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
     * with new Insertion marker info {@link CursorAnchorInfo#getInsertionMarkerFlags()},
     * {@link CursorAnchorInfo#getInsertionMarkerBaseline()}, etc whenever cursor/anchor position is
     * changed. To disable monitoring, call {@link InputConnection#requestCursorUpdates(int)} again
     * with this flag off.
     * <p>
     * This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
     * {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS} and update flags {@link #CURSOR_UPDATE_IMMEDIATE}
     * and {@link #CURSOR_UPDATE_MONITOR}.
     * </p>
     */
    int CURSOR_UPDATE_FILTER_INSERTION_MARKER = 1 << 4;

    /**
     * Called by the input method to ask the editor for calling back
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} to
     * notify cursor/anchor locations.
     *
     * @param cursorUpdateMode {@link #CURSOR_UPDATE_IMMEDIATE} and/or
     * {@link #CURSOR_UPDATE_MONITOR}. Pass {@code 0} to disable the effect of
     * {@link #CURSOR_UPDATE_MONITOR}.
     * @param cursorUpdateMode any combination of update modes and filters:
     * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and date filters:
     * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
     * Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
     * rejected and method will return {@code false}.
     * @return {@code true} if the request is scheduled. {@code false} to indicate that when the
     *         application will not call {@link InputMethodManager#updateCursorAnchorInfo(
     *         android.view.View, CursorAnchorInfo)}.
+15 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view.inputmethod;

import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS;
import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_IMMEDIATE;
import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_MONITOR;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
@@ -1797,11 +1798,12 @@ public final class InputMethodManager {
            }
            if (mServedInputConnection != null && getDelegate().hasActiveConnection(view)) {
                // TODO (b/210039666): optimize CURSOR_UPDATE_IMMEDIATE.
                // TODO (b/215533103): Introduce new modes in requestCursorUpdates().
                // TODO (b/210039666): Pipe IME displayId from InputBindResult and use it here.
                //  instead of mDisplayId.
                mServedInputConnection.requestCursorUpdatesFromImm(
                        CURSOR_UPDATE_IMMEDIATE | CURSOR_UPDATE_MONITOR, mDisplayId);
                        CURSOR_UPDATE_IMMEDIATE | CURSOR_UPDATE_MONITOR
                                | CURSOR_UPDATE_FILTER_EDITOR_BOUNDS,
                        mDisplayId);
            }

            try {
@@ -2448,6 +2450,17 @@ public final class InputMethodManager {
        }
    }

    /**
     * Get the requested mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}.
     *
     * @hide
     */
    public int getUpdateCursorAnchorInfoMode() {
        synchronized (mH) {
            return mRequestUpdateCursorAnchorInfoMonitorMode;
        }
    }

    /**
     * Report the current cursor location in its window.
     *
+83 −59
Original line number Diff line number Diff line
@@ -4567,6 +4567,19 @@ public class Editor {
            if (layout == null) {
                return;
            }
            int mode = imm.getUpdateCursorAnchorInfoMode();
            boolean includeEditorBounds =
                    (mode & InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS) != 0;
            boolean includeCharacterBounds =
                    (mode & InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS) != 0;
            boolean includeInsertionMarker =
                    (mode & InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER) != 0;
            boolean includeAll =
                    (!includeEditorBounds && !includeCharacterBounds && !includeInsertionMarker);

            includeEditorBounds |= includeAll;
            includeCharacterBounds |= includeAll;
            includeInsertionMarker |= includeAll;

            final CursorAnchorInfo.Builder builder = mSelectionInfoBuilder;
            builder.reset();
@@ -4579,18 +4592,23 @@ public class Editor {
            mTextView.getLocationOnScreen(mTmpIntOffset);
            mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]);
            builder.setMatrix(mViewToScreenMatrix);

            if (includeEditorBounds) {
                final RectF bounds = new RectF();
                mTextView.getBoundsOnScreen(bounds, false /* clipToParent */);
                EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
                //TODO(b/210039666): add Handwriting bounds once they're available.
                builder.setEditorBoundsInfo(
                        boundsBuilder.setEditorBounds(bounds).build());
            }

            if (includeCharacterBounds || includeInsertionMarker) {
                final float viewportToContentHorizontalOffset =
                        mTextView.viewportToContentHorizontalOffset();
                final float viewportToContentVerticalOffset =
                        mTextView.viewportToContentVerticalOffset();

                if (includeCharacterBounds) {
                    final CharSequence text = mTextView.getText();
                    if (text instanceof Spannable) {
                        final Spannable sp = (Spannable) text;
@@ -4602,7 +4620,8 @@ public class Editor {
                            composingTextStart = temp;
                        }
                        final boolean hasComposingText =
                        (0 <= composingTextStart) && (composingTextStart < composingTextEnd);
                                (0 <= composingTextStart) && (composingTextStart
                                        < composingTextEnd);
                        if (hasComposingText) {
                            final CharSequence composingText = text.subSequence(composingTextStart,
                                    composingTextEnd);
@@ -4612,7 +4631,9 @@ public class Editor {
                                    viewportToContentVerticalOffset);
                        }
                    }
                }

                if (includeInsertionMarker) {
                    // Treat selectionStart as the insertion point.
                    if (0 <= selectionStart) {
                        final int offset = selectionStart;
@@ -4640,7 +4661,10 @@ public class Editor {
                            insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
                        }
                        builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
                        insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
                                insertionMarkerBaseline, insertionMarkerBottom,
                                insertionMarkerFlags);
                    }
                }
            }

            imm.updateCursorAnchorInfo(mTextView, builder.build());
+4 −1
Original line number Diff line number Diff line
@@ -211,7 +211,10 @@ public final class EditableInputConnection extends BaseInputConnection
        // It is possible that any other bit is used as a valid flag in a future release.
        // We should reject the entire request in such a case.
        final int knownFlagMask = InputConnection.CURSOR_UPDATE_IMMEDIATE
                | InputConnection.CURSOR_UPDATE_MONITOR;
                | InputConnection.CURSOR_UPDATE_MONITOR
                | InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS
                | InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER
                | InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS;
        final int unknownFlags = cursorUpdateMode & ~knownFlagMask;
        if (unknownFlags != 0) {
            if (DEBUG) {