From 93d47b04876033864e864c203ab1a7cb95e03d9d Mon Sep 17 00:00:00 2001 From: Kohsuke Yatoh Date: Wed, 13 Jan 2021 01:01:09 +0000 Subject: [PATCH] Add supportedAttributes param for spell checker. Bug: 166304720 Test: atest CtsInputMethodTestCases:SpellCheckerTest Change-Id: I22f4a59a0c8ccb43f799351062f9fabca42403cd --- core/api/current.txt | 4 +- .../textservice/SpellCheckerService.java | 38 ++++++++--- .../view/textservice/SuggestionsInfo.java | 28 ++++++-- .../view/textservice/TextServicesManager.java | 68 +++++++++++++++---- core/java/android/widget/SpellChecker.java | 6 +- .../textservice/ISpellCheckerService.aidl | 2 + .../textservice/ITextServicesManager.aidl | 2 +- .../TextServicesManagerService.java | 14 ++-- 8 files changed, 128 insertions(+), 34 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 3ada624dd2cc8..3eeee67ad2919 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -38197,6 +38197,7 @@ package android.service.textservice { ctor public SpellCheckerService.Session(); method public android.os.Bundle getBundle(); method public String getLocale(); + method public int getSupportedAttributes(); method public void onCancel(); method public void onClose(); method public abstract void onCreate(); @@ -51786,7 +51787,8 @@ package android.view.textservice { method @Nullable public android.view.textservice.SpellCheckerSubtype getCurrentSpellCheckerSubtype(boolean); method @Nullable public java.util.List getEnabledSpellCheckersList(); method public boolean isSpellCheckerEnabled(); - method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(android.os.Bundle, java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean); + method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean); + method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean, int); } } diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java index bd1b44ca18d01..e4925842ad85c 100644 --- a/core/java/android/service/textservice/SpellCheckerService.java +++ b/core/java/android/service/textservice/SpellCheckerService.java @@ -16,11 +16,6 @@ package android.service.textservice; -import com.android.internal.textservice.ISpellCheckerService; -import com.android.internal.textservice.ISpellCheckerServiceCallback; -import com.android.internal.textservice.ISpellCheckerSession; -import com.android.internal.textservice.ISpellCheckerSessionListener; - import android.app.Service; import android.content.Intent; import android.os.Bundle; @@ -34,6 +29,11 @@ import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; +import com.android.internal.textservice.ISpellCheckerService; +import com.android.internal.textservice.ISpellCheckerServiceCallback; +import com.android.internal.textservice.ISpellCheckerSession; +import com.android.internal.textservice.ISpellCheckerSessionListener; + import java.lang.ref.WeakReference; import java.text.BreakIterator; import java.util.ArrayList; @@ -231,6 +231,18 @@ public abstract class SpellCheckerService extends Service { public Bundle getBundle() { return mInternalSession.getBundle(); } + + /** + * Returns result attributes supported for this session. + * + *

The session implementation should not set attributes that are not included in the + * return value of {@code getSupportedAttributes()} when creating {@link SuggestionsInfo}. + * + * @return The supported result attributes for this session + */ + public @SuggestionsInfo.ResultAttrs int getSupportedAttributes() { + return mInternalSession.getSupportedAttributes(); + } } // Preventing from exposing ISpellCheckerSession.aidl, create an internal class. @@ -239,13 +251,16 @@ public abstract class SpellCheckerService extends Service { private final Session mSession; private final String mLocale; private final Bundle mBundle; + private final @SuggestionsInfo.ResultAttrs int mSupportedAttributes; public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener, - Bundle bundle, Session session) { + Bundle bundle, Session session, + @SuggestionsInfo.ResultAttrs int supportedAttributes) { mListener = listener; mSession = session; mLocale = locale; mBundle = bundle; + mSupportedAttributes = supportedAttributes; session.setInternalISpellCheckerSession(this); } @@ -303,6 +318,10 @@ public abstract class SpellCheckerService extends Service { public Bundle getBundle() { return mBundle; } + + public @SuggestionsInfo.ResultAttrs int getSupportedAttributes() { + return mSupportedAttributes; + } } private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub { @@ -323,11 +342,14 @@ public abstract class SpellCheckerService extends Service { * {@link Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} and * {@link Session#onGetSuggestions(TextInfo, int)} * @param bundle bundle to be returned from {@link Session#getBundle()} + * @param supportedAttributes A union of {@link SuggestionsInfo} attributes that the spell + * checker can set in the spell checking results. * @param callback IPC channel to return the result to the caller in an asynchronous manner */ @Override public void getISpellCheckerSession( String locale, ISpellCheckerSessionListener listener, Bundle bundle, + @SuggestionsInfo.ResultAttrs int supportedAttributes, ISpellCheckerServiceCallback callback) { final SpellCheckerService service = mInternalServiceRef.get(); final InternalISpellCheckerSession internalSession; @@ -337,8 +359,8 @@ public abstract class SpellCheckerService extends Service { internalSession = null; } else { final Session session = service.createSession(); - internalSession = - new InternalISpellCheckerSession(locale, listener, bundle, session); + internalSession = new InternalISpellCheckerSession( + locale, listener, bundle, session, supportedAttributes); session.onCreate(); } try { diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java index 1301c49c2ae02..775a6bdacd014 100644 --- a/core/java/android/view/textservice/SuggestionsInfo.java +++ b/core/java/android/view/textservice/SuggestionsInfo.java @@ -16,17 +16,37 @@ package android.view.textservice; +import android.annotation.IntDef; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.util.ArrayUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * This class contains a metadata of suggestions from the text service */ public final class SuggestionsInfo implements Parcelable { private static final String[] EMPTY = ArrayUtils.emptyArray(String.class); + /** + * An internal annotation to indicate that one ore more combinations of + * RESULT_ATTR_ flags are expected. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "RESULT_ATTR_" }, value = { + RESULT_ATTR_IN_THE_DICTIONARY, + RESULT_ATTR_LOOKS_LIKE_TYPO, + RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS, + RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR, + RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS, + }) + public @interface ResultAttrs {} + /** * Flag of the attributes of the suggestions that can be obtained by * {@link #getSuggestionsAttributes}: this tells that the requested word was found @@ -63,7 +83,7 @@ public final class SuggestionsInfo implements Parcelable { */ public static final int RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS = 0x0010; - private final int mSuggestionsAttributes; + private final @ResultAttrs int mSuggestionsAttributes; private final String[] mSuggestions; private final boolean mSuggestionsAvailable; private int mCookie; @@ -85,8 +105,8 @@ public final class SuggestionsInfo implements Parcelable { * @param cookie the cookie of the input TextInfo * @param sequence the cookie of the input TextInfo */ - public SuggestionsInfo( - int suggestionsAttributes, String[] suggestions, int cookie, int sequence) { + public SuggestionsInfo(@ResultAttrs int suggestionsAttributes, String[] suggestions, int cookie, + int sequence) { if (suggestions == null) { mSuggestions = EMPTY; mSuggestionsAvailable = false; @@ -152,7 +172,7 @@ public final class SuggestionsInfo implements Parcelable { * in its dictionary or not and whether the spell checker has confident suggestions for the * word or not. */ - public int getSuggestionsAttributes() { + public @ResultAttrs int getSuggestionsAttributes() { return mSuggestionsAttributes; } diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index 578ed8c948e29..0a1aea38dd58f 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -18,6 +18,7 @@ package android.view.textservice; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemService; import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; @@ -139,21 +140,58 @@ public final class TextServicesManager { } /** - * Get a spell checker session for the specified spell checker - * @param locale the locale for the spell checker. If {@code locale} is null and - * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be - * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true, - * the locale specified in Settings will be returned only when it is same as {@code locale}. - * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is - * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be - * selected. - * @param listener a spell checker session lister for getting results from a spell checker. - * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled - * languages in settings will be returned. - * @return the spell checker session of the spell checker + * Get a spell checker session from the spell checker. + * + *

{@link SuggestionsInfo#RESULT_ATTR_IN_THE_DICTIONARY}, + * {@link SuggestionsInfo#RESULT_ATTR_LOOKS_LIKE_TYPO}, and + * {@link SuggestionsInfo#RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS} will be passed to the spell + * checker as supported attributes. + * + * @see #newSpellCheckerSession(Bundle, Locale, SpellCheckerSessionListener, boolean, int) + * @param bundle A bundle to pass to the spell checker. + * @param locale The locale for the spell checker. + * @param listener A spell checker session lister for getting results from the spell checker. + * @param referToSpellCheckerLanguageSettings If true, the session for one of enabled + * languages in settings will be used. + * @return A spell checker session from the spell checker. */ - public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale, - SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) { + @Nullable + public SpellCheckerSession newSpellCheckerSession(@Nullable Bundle bundle, + @Nullable Locale locale, + @NonNull SpellCheckerSessionListener listener, + boolean referToSpellCheckerLanguageSettings) { + return newSpellCheckerSession(bundle, locale, listener, referToSpellCheckerLanguageSettings, + SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY + | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO + | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS); + } + + /** + * Get a spell checker session from the spell checker. + * + *

If {@code locale} is null and {@code referToSpellCheckerLanguageSettings} is true, the + * locale specified in Settings will be used. If {@code locale} is not null and + * {@code referToSpellCheckerLanguageSettings} is true, the locale specified in Settings will be + * returned only when it is same as {@code locale}. + * Exceptionally, when {@code referToSpellCheckerLanguageSettings} is true and {@code locale} is + * language only (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be + * selected. + * + * @param bundle A bundle to pass to the spell checker. + * @param locale The locale for the spell checker. + * @param listener A spell checker session lister for getting results from a spell checker. + * @param referToSpellCheckerLanguageSettings If true, the session for one of enabled + * languages in settings will be used. + * @param supportedAttributes A union of {@link SuggestionsInfo} attributes that the spell + * checker can set in the spell checking results. + * @return The spell checker session of the spell checker. + */ + @Nullable + public SpellCheckerSession newSpellCheckerSession(@Nullable Bundle bundle, + @SuppressLint("UseIcu") @Nullable Locale locale, + @NonNull SpellCheckerSessionListener listener, + @SuppressLint("ListenerLast") boolean referToSpellCheckerLanguageSettings, + @SuppressLint("ListenerLast") @SuggestionsInfo.ResultAttrs int supportedAttributes) { if (listener == null) { throw new NullPointerException(); } @@ -210,7 +248,7 @@ public final class TextServicesManager { try { mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(), session.getTextServicesSessionListener(), - session.getSpellCheckerSessionListener(), bundle); + session.getSpellCheckerSessionListener(), bundle, supportedAttributes); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index b06fa1a058d44..97d98fd8ec592 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -129,7 +129,11 @@ public class SpellChecker implements SpellCheckerSessionListener { mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession( null /* Bundle not currently used by the textServicesManager */, mCurrentLocale, this, - false /* means any available languages from current spell checker */); + false /* means any available languages from current spell checker */, + SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY + | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO + | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR + | SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS); mIsSentenceSpellCheckSupported = true; } diff --git a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl index 6a25964586675..295107bb7179f 100644 --- a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl +++ b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl @@ -39,9 +39,11 @@ oneway interface ISpellCheckerService { * {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} and * {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} * @param bundle bundle to be returned from {@link android.service.textservice.SpellCheckerService.Session#getBundle()} + * @param supportedAttributes supported attributes to be returned from {@link android.service.textservice.SpellCheckerService.Session#getSupportedAttributes()} * @param callback IPC channel to return the result to the caller in an asynchronous manner */ void getISpellCheckerSession( String locale, ISpellCheckerSessionListener listener, in Bundle bundle, + int supportedAttributes, ISpellCheckerServiceCallback callback); } diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl index 8022949d17283..dce67e7ca3805 100644 --- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl +++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl @@ -34,7 +34,7 @@ interface ITextServicesManager { boolean allowImplicitlySelectedSubtype); oneway void getSpellCheckerService(int userId, String sciId, in String locale, in ITextServicesSessionListener tsListener, - in ISpellCheckerSessionListener scListener, in Bundle bundle); + in ISpellCheckerSessionListener scListener, in Bundle bundle, int supportedAttributes); oneway void finishSpellCheckerService(int userId, in ISpellCheckerSessionListener listener); boolean isSpellCheckerEnabled(int userId); SpellCheckerInfo[] getEnabledSpellCheckers(int userId); diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java index f39067b110a8a..cd2b8943ce117 100644 --- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java +++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java @@ -45,6 +45,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.textservice.SpellCheckerInfo; import android.view.textservice.SpellCheckerSubtype; +import android.view.textservice.SuggestionsInfo; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; @@ -58,7 +59,6 @@ import com.android.internal.textservice.ITextServicesSessionListener; import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import org.xmlpull.v1.XmlPullParserException; @@ -567,7 +567,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void getSpellCheckerService(@UserIdInt int userId, String sciId, String locale, ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener, - Bundle bundle) { + Bundle bundle, @SuggestionsInfo.ResultAttrs int supportedAttributes) { verifyUser(userId); if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) { Slog.e(TAG, "getSpellCheckerService: Invalid input."); @@ -603,7 +603,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { // Start getISpellCheckerSession async IPC, or just queue the request until the spell // checker service is bound. bindGroup.getISpellCheckerSessionOrQueueLocked( - new SessionRequest(uid, locale, tsListener, scListener, bundle)); + new SessionRequest(uid, locale, tsListener, scListener, bundle, + supportedAttributes)); } } @@ -774,15 +775,18 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public final ISpellCheckerSessionListener mScListener; @Nullable public final Bundle mBundle; + public final int mSupportedAttributes; SessionRequest(int uid, @Nullable String locale, @NonNull ITextServicesSessionListener tsListener, - @NonNull ISpellCheckerSessionListener scListener, @Nullable Bundle bundle) { + @NonNull ISpellCheckerSessionListener scListener, @Nullable Bundle bundle, + @SuggestionsInfo.ResultAttrs int supportedAttributes) { mUid = uid; mLocale = locale; mTsListener = tsListener; mScListener = scListener; mBundle = bundle; + mSupportedAttributes = supportedAttributes; } } @@ -825,6 +829,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { final SessionRequest request = mPendingSessionRequests.get(i); mSpellChecker.getISpellCheckerSession( request.mLocale, request.mScListener, request.mBundle, + request.mSupportedAttributes, new ISpellCheckerServiceCallbackBinder(this, request)); mOnGoingSessionRequests.add(request); } @@ -913,6 +918,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { try { mSpellChecker.getISpellCheckerSession( request.mLocale, request.mScListener, request.mBundle, + request.mSupportedAttributes, new ISpellCheckerServiceCallbackBinder(this, request)); mOnGoingSessionRequests.add(request); } catch(RemoteException e) {