Merge "InputMethodInfo attr to suppress spell checker." into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 9f5255b..ef93f45 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1394,6 +1394,7 @@
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 @@
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;
}
@@ -51491,6 +51493,7 @@
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 5d876a6..cc533eb 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -60,6 +60,7 @@
* @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";
@@ -118,6 +119,11 @@
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
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -160,6 +166,7 @@
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 @@
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 @@
mIsAuxIme = isAuxIme;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+ mSuppressesSpellChecker = suppressesSpellChecker;
mIsVrOnly = isVrOnly;
}
@@ -284,6 +294,7 @@
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 @@
mForceDefault = forceDefault;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+ mSuppressesSpellChecker = false;
mIsVrOnly = isVrOnly;
}
@@ -494,7 +506,8 @@
+ " mSettingsActivityName=" + mSettingsActivityName
+ " mIsVrOnly=" + mIsVrOnly
+ " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
- + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled);
+ + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+ + " mSuppressesSpellChecker=" + mSuppressesSpellChecker);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -563,6 +576,13 @@
}
/**
+ * Return {@code true} if this input method suppresses spell checker.
+ */
+ public boolean suppressesSpellChecker() {
+ return mSuppressesSpellChecker;
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
@@ -576,6 +596,7 @@
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 53bbc0a..c6e5eee 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -444,6 +444,13 @@
*/
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 @@
mCurId = res.id;
mBindSequence = res.sequence;
mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
+ mIsInputMethodSuppressingSpellChecker =
+ res.isInputMethodSuppressingSpellChecker;
}
startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
@@ -1470,6 +1479,15 @@
}
/**
+ * 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.
*/
void clearBindingLocked() {
@@ -1513,6 +1531,7 @@
@UnsupportedAppUsage
void finishInputLocked() {
mActivityViewToScreenMatrix = null;
+ mIsInputMethodSuppressingSpellChecker = false;
setNextServedViewLocked(null);
if (getServedViewLocked() != null) {
if (DEBUG) {
@@ -2037,6 +2056,7 @@
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 35d8445..ba58b65 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -25,6 +25,7 @@
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 @@
* @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 @@
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 996757d..6fb01a3 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.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 @@
@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 @@
@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 @@
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 @@
}
}
+ /** @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 7517b80..ca89677 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -988,6 +988,12 @@
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 f29e95c..c9755a3 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -204,6 +204,8 @@
@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 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 @@
mActivityViewToScreenMatrixValues = new float[9];
activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues);
}
+ this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker;
}
InputBindResult(Parcel source) {
@@ -245,6 +249,7 @@
id = source.readString();
sequence = source.readInt();
mActivityViewToScreenMatrixValues = source.createFloatArray();
+ isInputMethodSuppressingSpellChecker = source.readBoolean();
}
@Override
@@ -252,6 +257,7 @@
return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
+ " sequence=" + sequence
+ " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix()
+ + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
+ "}";
}
@@ -274,6 +280,7 @@
dest.writeString(id);
dest.writeInt(sequence);
dest.writeFloatArray(mActivityViewToScreenMatrixValues);
+ dest.writeBoolean(isInputMethodSuppressingSpellChecker);
}
/**
@@ -340,7 +347,7 @@
}
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 6ebd878..62278d5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -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
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 40c80db..0979ab55 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -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">
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6ab4a69..e9c3ec3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2381,9 +2381,12 @@
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 @@
// 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 @@
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 @@
// 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 @@
}
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 @@
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1, null);
+ null, null, null, -1, null, false);
}
mCurFocusedWindow = windowToken;
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 1dd3d41..ef1489b 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1748,7 +1748,7 @@
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 @@
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;