diff --git a/core/api/current.txt b/core/api/current.txt index 9f5255b9de98c..ef93f45e96eff 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1394,6 +1394,7 @@ package android { field public static final int supportsRtl = 16843695; // 0x10103af field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb field public static final int supportsUploading = 16843419; // 0x101029b + field public static final int suppressesSpellChecker = 16844354; // 0x1010642 field public static final int switchMinWidth = 16843632; // 0x1010370 field public static final int switchPadding = 16843633; // 0x1010371 field public static final int switchPreferenceStyle = 16843629; // 0x101036d @@ -51470,6 +51471,7 @@ package android.view.inputmethod { method public int getSubtypeCount(); method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public CharSequence loadLabel(android.content.pm.PackageManager); + method public boolean suppressesSpellChecker(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } @@ -51491,6 +51493,7 @@ package android.view.inputmethod { method public boolean isActive(android.view.View); method public boolean isActive(); method public boolean isFullscreenMode(); + method public boolean isInputMethodSuppressingSpellChecker(); method @Deprecated public boolean isWatchingCursor(android.view.View); method public void restartInput(android.view.View); method public void sendAppPrivateCommand(android.view.View, String, android.os.Bundle); diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 5d876a6f62d35..cc533eb3b5d73 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -60,6 +60,7 @@ import java.util.List; * @attr ref android.R.styleable#InputMethod_isDefault * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions + * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker */ public final class InputMethodInfo implements Parcelable { static final String TAG = "InputMethodInfo"; @@ -117,6 +118,11 @@ public final class InputMethodInfo implements Parcelable { */ private final boolean mInlineSuggestionsEnabled; + /** + * The flag whether this IME suppresses spell checker. + */ + private final boolean mSuppressesSpellChecker; + /** * @param service the {@link ResolveInfo} corresponds in which the IME is implemented. * @return a unique ID to be returned by {@link #getId()}. We have used @@ -160,6 +166,7 @@ public final class InputMethodInfo implements Parcelable { boolean isAuxIme = true; boolean supportsSwitchingToNextInputMethod = false; // false as default boolean inlineSuggestionsEnabled = false; // false as default + boolean suppressesSpellChecker = false; // false as default mForceDefault = false; PackageManager pm = context.getPackageManager(); @@ -203,6 +210,8 @@ public final class InputMethodInfo implements Parcelable { false); inlineSuggestionsEnabled = sa.getBoolean( com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false); + suppressesSpellChecker = sa.getBoolean( + com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false); sa.recycle(); final int depth = parser.getDepth(); @@ -274,6 +283,7 @@ public final class InputMethodInfo implements Parcelable { mIsAuxIme = isAuxIme; mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; mInlineSuggestionsEnabled = inlineSuggestionsEnabled; + mSuppressesSpellChecker = suppressesSpellChecker; mIsVrOnly = isVrOnly; } @@ -284,6 +294,7 @@ public final class InputMethodInfo implements Parcelable { mIsAuxIme = source.readInt() == 1; mSupportsSwitchingToNextInputMethod = source.readInt() == 1; mInlineSuggestionsEnabled = source.readInt() == 1; + mSuppressesSpellChecker = source.readBoolean(); mIsVrOnly = source.readBoolean(); mService = ResolveInfo.CREATOR.createFromParcel(source); mSubtypes = new InputMethodSubtypeArray(source); @@ -342,6 +353,7 @@ public final class InputMethodInfo implements Parcelable { mForceDefault = forceDefault; mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; mInlineSuggestionsEnabled = inlineSuggestionsEnabled; + mSuppressesSpellChecker = false; mIsVrOnly = isVrOnly; } @@ -494,7 +506,8 @@ public final class InputMethodInfo implements Parcelable { + " mSettingsActivityName=" + mSettingsActivityName + " mIsVrOnly=" + mIsVrOnly + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod - + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled); + + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled + + " mSuppressesSpellChecker=" + mSuppressesSpellChecker); pw.println(prefix + "mIsDefaultResId=0x" + Integer.toHexString(mIsDefaultResId)); pw.println(prefix + "Service:"); @@ -562,6 +575,13 @@ public final class InputMethodInfo implements Parcelable { return mInlineSuggestionsEnabled; } + /** + * Return {@code true} if this input method suppresses spell checker. + */ + public boolean suppressesSpellChecker() { + return mSuppressesSpellChecker; + } + /** * Used to package this object into a {@link Parcel}. * @@ -576,6 +596,7 @@ public final class InputMethodInfo implements Parcelable { dest.writeInt(mIsAuxIme ? 1 : 0); dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0); dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0); + dest.writeBoolean(mSuppressesSpellChecker); dest.writeBoolean(mIsVrOnly); mService.writeToParcel(dest, flags); mSubtypes.writeToParcel(dest); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 53bbc0ab1a02f..c6e5eee3568e0 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -444,6 +444,13 @@ public final class InputMethodManager { */ private Matrix mActivityViewToScreenMatrix = null; + /** + * As reported by {@link InputBindResult}. This value is determined by + * {@link com.android.internal.R.styleable#InputMethod_suppressesSpellChecking}. + */ + @GuardedBy("mH") + private boolean mIsInputMethodSuppressingSpellChecker = false; + // ----------------------------------------------------------- /** @@ -858,6 +865,8 @@ public final class InputMethodManager { mCurId = res.id; mBindSequence = res.sequence; mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix(); + mIsInputMethodSuppressingSpellChecker = + res.isInputMethodSuppressingSpellChecker; } startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0); return; @@ -1469,6 +1478,15 @@ public final class InputMethodManager { } } + /** + * Return {@code true} if the input method is suppressing system spell checker. + */ + public boolean isInputMethodSuppressingSpellChecker() { + synchronized (mH) { + return mIsInputMethodSuppressingSpellChecker; + } + } + /** * Reset all of the state associated with being bound to an input method. */ @@ -1513,6 +1531,7 @@ public final class InputMethodManager { @UnsupportedAppUsage void finishInputLocked() { mActivityViewToScreenMatrix = null; + mIsInputMethodSuppressingSpellChecker = false; setNextServedViewLocked(null); if (getServedViewLocked() != null) { if (DEBUG) { @@ -2037,6 +2056,7 @@ public final class InputMethodManager { return false; } mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix(); + mIsInputMethodSuppressingSpellChecker = res.isInputMethodSuppressingSpellChecker; if (res.id != null) { setInputChannelLocked(res.channel); mBindSequence = res.sequence; diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 35d84458bd35e..ba58b6525a6de 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -25,6 +25,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.util.Log; +import android.view.inputmethod.InputMethodManager; import com.android.internal.textservice.ISpellCheckerSession; import com.android.internal.textservice.ISpellCheckerSessionListener; @@ -176,6 +177,11 @@ public class SpellCheckerSession { * @param suggestionsLimit the maximum number of suggestions that will be returned */ public void getSentenceSuggestions(TextInfo[] textInfos, int suggestionsLimit) { + final InputMethodManager imm = mTextServicesManager.getInputMethodManager(); + if (imm != null && imm.isInputMethodSuppressingSpellChecker()) { + handleOnGetSentenceSuggestionsMultiple(new SentenceSuggestionsInfo[0]); + return; + } mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple( textInfos, suggestionsLimit); } @@ -204,6 +210,11 @@ public class SpellCheckerSession { if (DBG) { Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); } + final InputMethodManager imm = mTextServicesManager.getInputMethodManager(); + if (imm != null && imm.isInputMethodSuppressingSpellChecker()) { + handleOnGetSuggestionsMultiple(new SuggestionsInfo[0]); + return; + } mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( textInfos, suggestionsLimit, sequentialWords); } diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index 996757da06418..6fb01a309402d 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -30,6 +30,7 @@ import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.UserHandle; import android.util.Log; +import android.view.inputmethod.InputMethodManager; import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; import com.android.internal.textservice.ISpellCheckerSessionListener; @@ -88,10 +89,15 @@ public final class TextServicesManager { @UserIdInt private final int mUserId; - private TextServicesManager(@UserIdInt int userId) throws ServiceNotFoundException { + @Nullable + private final InputMethodManager mInputMethodManager; + + private TextServicesManager(@UserIdInt int userId, + @Nullable InputMethodManager inputMethodManager) throws ServiceNotFoundException { mService = ITextServicesManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE)); mUserId = userId; + mInputMethodManager = inputMethodManager; } /** @@ -105,7 +111,8 @@ public final class TextServicesManager { @NonNull public static TextServicesManager createInstance(@NonNull Context context) throws ServiceNotFoundException { - return new TextServicesManager(context.getUserId()); + return new TextServicesManager(context.getUserId(), context.getSystemService( + InputMethodManager.class)); } /** @@ -118,7 +125,7 @@ public final class TextServicesManager { synchronized (TextServicesManager.class) { if (sInstance == null) { try { - sInstance = new TextServicesManager(UserHandle.myUserId()); + sInstance = new TextServicesManager(UserHandle.myUserId(), null); } catch (ServiceNotFoundException e) { throw new IllegalStateException(e); } @@ -127,6 +134,12 @@ public final class TextServicesManager { } } + /** @hide */ + @Nullable + public InputMethodManager getInputMethodManager() { + return mInputMethodManager; + } + /** * Returns the language component of a given locale string. */ diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 7517b805da69d..ca8967794c815 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -988,6 +988,12 @@ public class Editor { if (mTextView.isTextEditable() && mTextView.isSuggestionsEnabled() && !(mTextView.isInExtractedMode())) { + final InputMethodManager imm = getInputMethodManager(); + if (imm != null && imm.isInputMethodSuppressingSpellChecker()) { + // Do not close mSpellChecker here as it may be reused when the current IME has been + // changed. + return; + } if (mSpellChecker == null && createSpellChecker) { mSpellChecker = new SpellChecker(mTextView); } diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index f29e95ccdf658..c9755a3dcedc7 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -204,6 +204,8 @@ public final class InputBindResult implements Parcelable { @Nullable private final float[] mActivityViewToScreenMatrixValues; + public final boolean isInputMethodSuppressingSpellChecker; + /** * @return {@link Matrix} that corresponds to {@link #mActivityViewToScreenMatrixValues}. * {@code null} if {@link #mActivityViewToScreenMatrixValues} is {@code null}. @@ -220,7 +222,8 @@ public final class InputBindResult implements Parcelable { public InputBindResult(@ResultCode int _result, IInputMethodSession _method, InputChannel _channel, String _id, int _sequence, - @Nullable Matrix activityViewToScreenMatrix) { + @Nullable Matrix activityViewToScreenMatrix, + boolean isInputMethodSuppressingSpellChecker) { result = _result; method = _method; channel = _channel; @@ -232,6 +235,7 @@ public final class InputBindResult implements Parcelable { mActivityViewToScreenMatrixValues = new float[9]; activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues); } + this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker; } InputBindResult(Parcel source) { @@ -245,6 +249,7 @@ public final class InputBindResult implements Parcelable { id = source.readString(); sequence = source.readInt(); mActivityViewToScreenMatrixValues = source.createFloatArray(); + isInputMethodSuppressingSpellChecker = source.readBoolean(); } @Override @@ -252,6 +257,7 @@ public final class InputBindResult implements Parcelable { return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id + " sequence=" + sequence + " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix() + + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker + "}"; } @@ -274,6 +280,7 @@ public final class InputBindResult implements Parcelable { dest.writeString(id); dest.writeInt(sequence); dest.writeFloatArray(mActivityViewToScreenMatrixValues); + dest.writeBoolean(isInputMethodSuppressingSpellChecker); } /** @@ -340,7 +347,7 @@ public final class InputBindResult implements Parcelable { } private static InputBindResult error(@ResultCode int result) { - return new InputBindResult(result, null, null, null, -1, null); + return new InputBindResult(result, null, null, null, -1, null, false); } /** diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6ebd8786b5f13..62278d5b86f17 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3564,6 +3564,7 @@ +