Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72121457 authored by Kohsuke Yatoh's avatar Kohsuke Yatoh
Browse files

Add Executor param to TSM#newSpellCheckerSession().

SpellCheckerSessionListener callback methods will be called on
the provided executor.

This CL also adds more description for 'bundle' parameter.

Bug: 179804789
Test: atest CtsInputMethodTestCases:SpellCheckerTest
Change-Id: I43f9f04fc335ad45d2580f4711b19aee214bc7dd
parent e5262784
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -52290,7 +52290,7 @@ package android.view.textservice {
    method @Nullable public java.util.List<android.view.textservice.SpellCheckerInfo> getEnabledSpellCheckerInfos();
    method public boolean isSpellCheckerEnabled();
    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);
    method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable java.util.Locale, boolean, int, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener);
  }
}
+37 −35
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view.textservice;

import android.annotation.BinderThread;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
@@ -27,6 +29,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.textservice.ISpellCheckerSession;
import com.android.internal.textservice.ISpellCheckerSessionListener;
import com.android.internal.textservice.ITextServicesSessionListener;
@@ -35,6 +38,7 @@ import dalvik.system.CloseGuard;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executor;

/**
 * The SpellCheckerSession interface provides the per client functionality of SpellCheckerService.
@@ -102,38 +106,26 @@ public class SpellCheckerSession {
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private final SpellCheckerSessionListener mSpellCheckerSessionListener;
    private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
    private final Executor mExecutor;

    private final CloseGuard mGuard = CloseGuard.get();

    /** Handler that will execute the main tasks */
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ON_GET_SUGGESTION_MULTIPLE:
                    handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
                    break;
                case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
                    handleOnGetSentenceSuggestionsMultiple((SentenceSuggestionsInfo[]) msg.obj);
                    break;
            }
        }
    };

    /**
     * Constructor
     * @hide
     */
    public SpellCheckerSession(
            SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener) {
            SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener,
            Executor executor) {
        if (info == null || listener == null || tsm == null) {
            throw new NullPointerException();
        }
        mSpellCheckerInfo = info;
        mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler);
        mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(this);
        mInternalListener = new InternalListener(mSpellCheckerSessionListenerImpl);
        mTextServicesManager = tsm;
        mSpellCheckerSessionListener = listener;
        mExecutor = executor;

        mGuard.open("finishSession");
    }
@@ -219,12 +211,13 @@ public class SpellCheckerSession {
                textInfos, suggestionsLimit, sequentialWords);
    }

    private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) {
        mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
    void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionsInfos) {
        mExecutor.execute(() -> mSpellCheckerSessionListener.onGetSuggestions(suggestionsInfos));
    }

    private void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionInfos) {
        mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionInfos);
    void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionsInfos) {
        mExecutor.execute(() ->
                mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionsInfos));
    }

    private static final class SpellCheckerSessionListenerImpl
@@ -249,7 +242,8 @@ public class SpellCheckerSession {
        }

        private final Queue<SpellCheckerParams> mPendingTasks = new LinkedList<>();
        private Handler mHandler;
        @GuardedBy("SpellCheckerSessionListenerImpl.this")
        private SpellCheckerSession mSpellCheckerSession;

        private static final int STATE_WAIT_CONNECTION = 0;
        private static final int STATE_CONNECTED = 1;
@@ -270,8 +264,8 @@ public class SpellCheckerSession {
        private HandlerThread mThread;
        private Handler mAsyncHandler;

        public SpellCheckerSessionListenerImpl(Handler handler) {
            mHandler = handler;
        SpellCheckerSessionListenerImpl(SpellCheckerSession spellCheckerSession) {
            mSpellCheckerSession = spellCheckerSession;
        }

        private static class SpellCheckerParams {
@@ -349,6 +343,7 @@ public class SpellCheckerSession {
            }
        }

        @GuardedBy("SpellCheckerSessionListenerImpl.this")
        private void processCloseLocked() {
            if (DBG) Log.d(TAG, "entering processCloseLocked:"
                    + " session" + (mISpellCheckerSession != null ? ".hashCode()=#"
@@ -358,7 +353,7 @@ public class SpellCheckerSession {
            if (mThread != null) {
                mThread.quit();
            }
            mHandler = null;
            mSpellCheckerSession = null;
            mPendingTasks.clear();
            mThread = null;
            mAsyncHandler = null;
@@ -502,23 +497,30 @@ public class SpellCheckerSession {
            processTask(session, scp, false);
        }

        @BinderThread
        @Override
        public void onGetSuggestions(SuggestionsInfo[] results) {
            synchronized (this) {
                if (mHandler != null) {
                    mHandler.sendMessage(Message.obtain(mHandler,
                            MSG_ON_GET_SUGGESTION_MULTIPLE, results));
                }
            SpellCheckerSession session = getSpellCheckerSession();
            if (session != null) {
                // Lock should not be held when calling callback, in order to avoid deadlock.
                session.handleOnGetSuggestionsMultiple(results);
            }
        }

        @BinderThread
        @Override
        public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
            synchronized (this) {
                if (mHandler != null) {
                    mHandler.sendMessage(Message.obtain(mHandler,
                            MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
            SpellCheckerSession session = getSpellCheckerSession();
            if (session != null) {
                // Lock should not be held when calling callback, in order to avoid deadlock.
                session.handleOnGetSentenceSuggestionsMultiple(results);
            }
        }

        @Nullable
        private SpellCheckerSession getSpellCheckerSession() {
            synchronized (SpellCheckerSessionListenerImpl.this) {
                return mSpellCheckerSession;
            }
        }
    }
+30 −15
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view.textservice;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -25,6 +26,8 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -39,6 +42,8 @@ import com.android.internal.textservice.ITextServicesManager;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * System API to the overall text services, which arbitrates interaction between applications
@@ -160,10 +165,12 @@ public final class TextServicesManager {
     * {@link SuggestionsInfo#RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS} will be passed to the spell
     * checker as supported attributes.
     *
     * @see #newSpellCheckerSession(Bundle, Locale, SpellCheckerSessionListener, boolean, int)
     * @see #newSpellCheckerSession(Locale, boolean, int, Bundle, Executor,
     *      SpellCheckerSessionListener)
     * @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.
     *                 The listener will be called on the calling thread.
     * @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.
@@ -173,10 +180,15 @@ public final class TextServicesManager {
            @Nullable Locale locale,
            @NonNull SpellCheckerSessionListener listener,
            boolean referToSpellCheckerLanguageSettings) {
        return newSpellCheckerSession(bundle, locale, listener, referToSpellCheckerLanguageSettings,
                SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
        // Attributes existed before {@link #newSpellCheckerSession(Locale, boolean, int, Bundle,
        // Executor, SpellCheckerSessionListener)} was introduced.
        int supportedAttributes = SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
                | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
                        | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS);
                | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS;
        // Using the implicit looper to preserve the old behavior.
        Executor executor = new HandlerExecutor(new Handler());
        return newSpellCheckerSession(locale, referToSpellCheckerLanguageSettings,
                supportedAttributes, bundle, executor, listener);
    }

    /**
@@ -190,25 +202,28 @@ public final class TextServicesManager {
     * 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.
     * @param bundle A bundle for passing implementation-specific extra parameters for the spell
     *               checker. You can check the current spell checker package by
     *               {@link #getCurrentSpellCheckerInfo()}.
     * @param executor An executor to call the listener on.
     * @param listener A spell checker session lister for getting results from a spell checker.
     * @return The spell checker session of the spell checker.
     */
    @Nullable
    public SpellCheckerSession newSpellCheckerSession(
            @SuppressLint("NullableCollection") @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();
        }
            boolean referToSpellCheckerLanguageSettings,
            @SuggestionsInfo.ResultAttrs int supportedAttributes,
            @Nullable Bundle bundle,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull SpellCheckerSessionListener listener) {
        Objects.requireNonNull(executor);
        Objects.requireNonNull(listener);
        if (!referToSpellCheckerLanguageSettings && locale == null) {
            throw new IllegalArgumentException("Locale should not be null if you don't refer"
                    + " settings.");
@@ -258,7 +273,7 @@ public final class TextServicesManager {
        if (subtypeInUse == null) {
            return null;
        }
        final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener);
        final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener, executor);
        try {
            mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(),
                    session.getTextServicesSessionListener(),
+6 −6
Original line number Diff line number Diff line
@@ -126,14 +126,14 @@ public class SpellChecker implements SpellCheckerSessionListener {
                || mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) {
            mSpellCheckerSession = null;
        } else {
            mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession(
                    null /* Bundle not currently used by the textServicesManager */,
                    mCurrentLocale, this,
                    false /* means any available languages from current spell checker */,
                    SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
            int supportedAttributes = 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);
                    | SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS;
            mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession(
                    mCurrentLocale, false, supportedAttributes,
                    null /* Bundle not currently used by the textServicesManager */,
                    mTextView.getContext().getMainExecutor(), this);
        }

        // Restore SpellCheckSpans in pool