InputMethodInfo attr to suppress spell checker.
If InputMethodService declares suppressesSpellChecker="true", the system SpellCheckerService will be disabled. With TextView, when IMSs are switched while the user is editing, SuggestionSpans that were previously added will not be removed even if the next IMS declares suppressesSpellChecker="true". This is because we don't know if the SuggestionSpans were from Spell Checker, App, or IMS. Bug: 153473490 Test: atest CtsInputMethodTestCases:SpellCheckerTest Change-Id: Ia22b8758111087818beea8c07b1c173f1a94b8e4
This commit is contained in:
@@ -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
|
||||
@@ -51469,6 +51470,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<android.view.inputmethod.InputMethodInfo> CREATOR;
|
||||
}
|
||||
@@ -51490,6 +51492,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3564,6 +3564,7 @@
|
||||
<attr name="__removed2" format="boolean" />
|
||||
<!-- Specifies whether the IME supports showing inline suggestions. -->
|
||||
<attr name="supportsInlineSuggestions" format="boolean" />
|
||||
<attr name="suppressesSpellChecker" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
|
||||
|
||||
@@ -3088,6 +3088,7 @@
|
||||
<public name="selectableAsDefault"/>
|
||||
<public name="isAccessibilityTool"/>
|
||||
<public name="attributionTags"/>
|
||||
<public name="suppressesSpellChecker" />
|
||||
</public-group>
|
||||
|
||||
<public-group type="drawable" first-id="0x010800b5">
|
||||
|
||||
@@ -2381,9 +2381,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
|
||||
SoftInputShowHideReason.ATTACH_NEW_INPUT);
|
||||
}
|
||||
final InputMethodInfo curInputMethodInfo = mMethodMap.get(mCurId);
|
||||
final boolean suppressesSpellChecker =
|
||||
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
|
||||
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
|
||||
session.session, (session.channel != null ? session.channel.dup() : null),
|
||||
mCurId, mCurSeq, mCurActivityViewToScreenMatrix);
|
||||
mCurId, mCurSeq, mCurActivityViewToScreenMatrix, suppressesSpellChecker);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -2425,7 +2428,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
// party code.
|
||||
return new InputBindResult(
|
||||
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
|
||||
null, null, mCurMethodId, mCurSeq, null);
|
||||
null, null, mCurMethodId, mCurSeq, null, false);
|
||||
}
|
||||
|
||||
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
|
||||
@@ -2501,7 +2504,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
requestClientSessionLocked(cs);
|
||||
return new InputBindResult(
|
||||
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
|
||||
null, null, mCurId, mCurSeq, null);
|
||||
null, null, mCurId, mCurSeq, null, false);
|
||||
} else if (SystemClock.uptimeMillis()
|
||||
< (mLastBindTime+TIME_TO_RECONNECT)) {
|
||||
// In this case we have connected to the service, but
|
||||
@@ -2513,7 +2516,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
// to see if we can get back in touch with the service.
|
||||
return new InputBindResult(
|
||||
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
|
||||
null, null, mCurId, mCurSeq, null);
|
||||
null, null, mCurId, mCurSeq, null, false);
|
||||
} else {
|
||||
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
|
||||
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
|
||||
@@ -2553,7 +2556,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
}
|
||||
return new InputBindResult(
|
||||
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
|
||||
null, null, mCurId, mCurSeq, null);
|
||||
null, null, mCurId, mCurSeq, null, false);
|
||||
}
|
||||
mCurIntent = null;
|
||||
Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
|
||||
@@ -3509,7 +3512,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
}
|
||||
return new InputBindResult(
|
||||
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
|
||||
null, null, null, -1, null);
|
||||
null, null, null, -1, null, false);
|
||||
}
|
||||
|
||||
mCurFocusedWindow = windowToken;
|
||||
|
||||
@@ -1748,7 +1748,7 @@ public final class MultiClientInputMethodManagerService {
|
||||
return new InputBindResult(
|
||||
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
|
||||
null, null, data.mCurrentInputMethodInfo.getId(),
|
||||
clientInfo.mBindingSequence, null);
|
||||
clientInfo.mBindingSequence, null, false);
|
||||
case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
|
||||
case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
|
||||
clientInfo.mBindingSequence++;
|
||||
@@ -1770,7 +1770,7 @@ public final class MultiClientInputMethodManagerService {
|
||||
clientInfo.mInputMethodSession,
|
||||
clientInfo.mWriteChannel.dup(),
|
||||
data.mCurrentInputMethodInfo.getId(),
|
||||
clientInfo.mBindingSequence, null);
|
||||
clientInfo.mBindingSequence, null, false);
|
||||
case InputMethodClientState.UNREGISTERED:
|
||||
Slog.e(TAG, "The client is already unregistered.");
|
||||
return InputBindResult.INVALID_CLIENT;
|
||||
|
||||
Reference in New Issue
Block a user