summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorTaran Singh <tarandeep@google.com>2022-01-20 22:36:14 +0000
committerTaran Singh <tarandeep@google.com>2022-01-26 15:41:27 +0000
commit7fbcbf27c79dc7203236c13e1daee48bbb970fd5 (patch)
tree7832d0a64620f1a7c2e88eb44f2baac71844af75 /core/java
parent342509f09402284912fb894175ad414b977a81b8 (diff)
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
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java65
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java17
-rw-r--r--core/java/android/widget/Editor.java142
-rw-r--r--core/java/com/android/internal/inputmethod/EditableInputConnection.java5
4 files changed, 164 insertions, 65 deletions
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index b85fe7c3aae2..78509fe6e0b5 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -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)}.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 849f3c95d191..f480b24bdba3 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -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 {
@@ -2449,6 +2451,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.
*
* @deprecated Use {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} instead.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index dd70d69a8e02..3a7a544a334b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -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,68 +4592,79 @@ public class Editor {
mTextView.getLocationOnScreen(mTmpIntOffset);
mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]);
builder.setMatrix(mViewToScreenMatrix);
- 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());
-
- final float viewportToContentHorizontalOffset =
- mTextView.viewportToContentHorizontalOffset();
- final float viewportToContentVerticalOffset =
- mTextView.viewportToContentVerticalOffset();
-
- final CharSequence text = mTextView.getText();
- if (text instanceof Spannable) {
- final Spannable sp = (Spannable) text;
- int composingTextStart = EditableInputConnection.getComposingSpanStart(sp);
- int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp);
- if (composingTextEnd < composingTextStart) {
- final int temp = composingTextEnd;
- composingTextEnd = composingTextStart;
- composingTextStart = temp;
- }
- final boolean hasComposingText =
- (0 <= composingTextStart) && (composingTextStart < composingTextEnd);
- if (hasComposingText) {
- final CharSequence composingText = text.subSequence(composingTextStart,
- composingTextEnd);
- builder.setComposingText(composingTextStart, composingText);
- mTextView.populateCharacterBounds(builder, composingTextStart,
- composingTextEnd, viewportToContentHorizontalOffset,
- viewportToContentVerticalOffset);
- }
- }
- // Treat selectionStart as the insertion point.
- if (0 <= selectionStart) {
- final int offset = selectionStart;
- final int line = layout.getLineForOffset(offset);
- final float insertionMarkerX = layout.getPrimaryHorizontal(offset)
- + viewportToContentHorizontalOffset;
- final float insertionMarkerTop = layout.getLineTop(line)
- + viewportToContentVerticalOffset;
- final float insertionMarkerBaseline = layout.getLineBaseline(line)
- + viewportToContentVerticalOffset;
- final float insertionMarkerBottom = layout.getLineBottomWithoutSpacing(line)
- + viewportToContentVerticalOffset;
- final boolean isTopVisible = mTextView
- .isPositionVisible(insertionMarkerX, insertionMarkerTop);
- final boolean isBottomVisible = mTextView
- .isPositionVisible(insertionMarkerX, insertionMarkerBottom);
- int insertionMarkerFlags = 0;
- if (isTopVisible || isBottomVisible) {
- insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
- }
- if (!isTopVisible || !isBottomVisible) {
- insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+ 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;
+ int composingTextStart = EditableInputConnection.getComposingSpanStart(sp);
+ int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp);
+ if (composingTextEnd < composingTextStart) {
+ final int temp = composingTextEnd;
+ composingTextEnd = composingTextStart;
+ composingTextStart = temp;
+ }
+ final boolean hasComposingText =
+ (0 <= composingTextStart) && (composingTextStart
+ < composingTextEnd);
+ if (hasComposingText) {
+ final CharSequence composingText = text.subSequence(composingTextStart,
+ composingTextEnd);
+ builder.setComposingText(composingTextStart, composingText);
+ mTextView.populateCharacterBounds(builder, composingTextStart,
+ composingTextEnd, viewportToContentHorizontalOffset,
+ viewportToContentVerticalOffset);
+ }
+ }
}
- if (layout.isRtlCharAt(offset)) {
- insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+
+ if (includeInsertionMarker) {
+ // Treat selectionStart as the insertion point.
+ if (0 <= selectionStart) {
+ final int offset = selectionStart;
+ final int line = layout.getLineForOffset(offset);
+ final float insertionMarkerX = layout.getPrimaryHorizontal(offset)
+ + viewportToContentHorizontalOffset;
+ final float insertionMarkerTop = layout.getLineTop(line)
+ + viewportToContentVerticalOffset;
+ final float insertionMarkerBaseline = layout.getLineBaseline(line)
+ + viewportToContentVerticalOffset;
+ final float insertionMarkerBottom = layout.getLineBottomWithoutSpacing(line)
+ + viewportToContentVerticalOffset;
+ final boolean isTopVisible = mTextView
+ .isPositionVisible(insertionMarkerX, insertionMarkerTop);
+ final boolean isBottomVisible = mTextView
+ .isPositionVisible(insertionMarkerX, insertionMarkerBottom);
+ int insertionMarkerFlags = 0;
+ if (isTopVisible || isBottomVisible) {
+ insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+ }
+ if (!isTopVisible || !isBottomVisible) {
+ insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+ }
+ if (layout.isRtlCharAt(offset)) {
+ insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+ }
+ builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
+ insertionMarkerBaseline, insertionMarkerBottom,
+ insertionMarkerFlags);
+ }
}
- builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
- insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
}
imm.updateCursorAnchorInfo(mTextView, builder.build());
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 29bb3111d83e..630c271d9418 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -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) {