From e751e1036a0467e3aa2c8ecd23266b65eccea4d7 Mon Sep 17 00:00:00 2001 From: Kohsuke Yatoh Date: Thu, 3 Dec 2020 00:10:19 +0000 Subject: [PATCH] Add InputConnection#performSpellCheck(). IME can use this method to redo spell checking after it has learned a new user dictionary word. Bug: 166304720 Test: atest CtsInputMethodTestCases:SpellCheckerTest Change-Id: I956cd46f25bb77b7e1a6a3e1478c0d7efa1a056a --- core/api/current.txt | 1 + .../view/inputmethod/InputConnection.java | 14 +++++++++++ .../inputmethod/InputConnectionWrapper.java | 9 +++++++ core/java/android/widget/SpellChecker.java | 21 ++++++++++++++++ core/java/android/widget/TextView.java | 7 ++++++ .../view/IInputConnectionWrapper.java | 24 +++++++++++++++++++ .../android/internal/view/IInputContext.aidl | 2 ++ .../internal/view/InputConnectionWrapper.java | 11 +++++++++ .../widget/EditableInputConnection.java | 6 +++++ 9 files changed, 95 insertions(+) diff --git a/core/api/current.txt b/core/api/current.txt index e6585916521ac..99bd8dacb775f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -51192,6 +51192,7 @@ package android.view.inputmethod { method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); + method public default boolean performSpellCheck(); method public boolean reportFullscreenMode(boolean); method public boolean requestCursorUpdates(int); method public boolean sendKeyEvent(android.view.KeyEvent); diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 14b5d7c72d2f8..8c81143e4cdc9 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -857,6 +857,20 @@ public interface InputConnection { */ boolean reportFullscreenMode(boolean enabled); + /** + * Have the editor perform spell checking around the current selection. + * + *

The editor can ignore this method call if it does not support spell checking. + * + * @return For editor authors, the return value will always be ignored. For IME authors, this + * method returns true if the spell check request was sent (whether or not the + * associated editor supports spell checking), false if the input connection is no + * longer valid. + */ + default boolean performSpellCheck() { + return false; + } + /** * API to send private commands from an input method to its * connected editor. This can be used to provide domain-specific diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index 77956d15e399c..ca853485d4f39 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -282,6 +282,15 @@ public class InputConnectionWrapper implements InputConnection { return mTarget.reportFullscreenMode(enabled); } + /** + * {@inheritDoc} + * @throws NullPointerException if the target is {@code null}. + */ + @Override + public boolean performSpellCheck() { + return mTarget.performSpellCheck(); + } + /** * {@inheritDoc} * @throws NullPointerException if the target is {@code null}. diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 0611bea7978de..0788635d1c68d 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -215,6 +215,27 @@ public class SpellChecker implements SpellCheckerSessionListener { spellCheck(); } + void onPerformSpellCheck() { + final int selectionStart = mTextView.getSelectionStart(); + final int selectionEnd = mTextView.getSelectionEnd(); + final int selectionRangeStart; + final int selectionRangeEnd; + if (selectionStart < selectionEnd) { + selectionRangeStart = selectionStart; + selectionRangeEnd = selectionEnd; + } else { + selectionRangeStart = selectionEnd; + selectionRangeEnd = selectionStart; + } + // Expand the range so that it (hopefully) includes the current sentence. + final int start = Math.max(0, selectionRangeStart - MIN_SENTENCE_LENGTH); + final int end = Math.min(mTextView.length(), selectionRangeEnd + MIN_SENTENCE_LENGTH); + if (DBG) { + Log.d(TAG, "performSpellCheckAroundSelection: " + start + ", " + end); + } + spellCheck(start, end); + } + public void spellCheck(int start, int end) { if (DBG) { Log.d(TAG, "Start spell-checking: " + start + ", " + end); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 02a9300179062..ffe1b382cfdaa 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8913,6 +8913,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // intentionally empty } + /** @hide */ + public void onPerformSpellCheck() { + if (mEditor != null && mEditor.mSpellChecker != null) { + mEditor.mSpellChecker.onPerformSpellCheck(); + } + } + /** * Called by the framework in response to a private command from the * current method, provided by it calling diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 1d5935db63225..3a7e66ce76f08 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -72,6 +72,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81; private static final int DO_BEGIN_BATCH_EDIT = 90; private static final int DO_END_BATCH_EDIT = 95; + private static final int DO_PERFORM_SPELL_CHECK = 110; private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; @@ -234,6 +235,15 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessage(DO_END_BATCH_EDIT)); } + /** + * Dispatches the request for performing spell check. + * + * @see InputConnection#performSpellCheck() + */ + public void performSpellCheck() { + dispatchMessage(obtainMessage(DO_PERFORM_SPELL_CHECK)); + } + public void performPrivateCommand(String action, Bundle data) { dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data)); } @@ -681,6 +691,20 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } return; } + case DO_PERFORM_SPELL_CHECK: { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performSpellCheck on inactive InputConnection"); + return; + } + ic.performSpellCheck(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + return; + } case DO_PERFORM_PRIVATE_COMMAND: { final SomeArgs args = (SomeArgs) msg.obj; Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand"); diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 074908acdf2aa..53cbf961fff4b 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -70,6 +70,8 @@ import com.android.internal.inputmethod.ISurroundingTextResultCallback; void clearMetaKeyStates(int states); + void performSpellCheck(); + void performPrivateCommand(String action, in Bundle data); void setComposingRegion(int start, int end); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index b70348a953841..af9c0124078a2 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -441,6 +441,17 @@ public class InputConnectionWrapper implements InputConnection { return false; } + @AnyThread + @Override + public boolean performSpellCheck() { + try { + mIInputContext.performSpellCheck(); + return true; + } catch (RemoteException e) { + return false; + } + } + @AnyThread public boolean performPrivateCommand(String action, Bundle data) { try { diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 6cdf0df17b65c..767ad42efbf3c 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -177,6 +177,12 @@ public class EditableInputConnection extends BaseInputConnection return null; } + @Override + public boolean performSpellCheck() { + mTextView.onPerformSpellCheck(); + return true; + } + @Override public boolean performPrivateCommand(String action, Bundle data) { mTextView.onPrivateIMECommand(action, data);