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

Commit afb1a9e2 authored by satok's avatar satok Committed by Android (Google) Code Review
Browse files

Merge "add sentence level spell checker hidden apis"

parents 8eb46367 0dc1f648
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -137,6 +137,25 @@ public abstract class SpellCheckerService extends Service {
            return retval;
        }

        /**
         * @hide
         * The default implementation returns an array of SuggestionsInfo by simply calling
         * onGetSuggestions().
         * When you override this method, make sure that suggestionsLimit is applied to suggestions
         * that share the same start position and length.
         */
        public SuggestionsInfo[] onGetSuggestionsMultipleForSentence(TextInfo[] textInfos,
                int suggestionsLimit) {
            final int length = textInfos.length;
            final SuggestionsInfo[] retval = new SuggestionsInfo[length];
            for (int i = 0; i < length; ++i) {
                retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
                retval[i].setCookieAndSequence(
                        textInfos[i].getCookie(), textInfos[i].getSequence());
            }
            return retval;
        }

        /**
         * Request to abort all tasks executed in SpellChecker.
         * This function will run on the incoming IPC thread.
@@ -195,6 +214,16 @@ public abstract class SpellCheckerService extends Service {
            }
        }

        @Override
        public void onGetSuggestionsMultipleForSentence(
                TextInfo[] textInfos, int suggestionsLimit) {
            try {
                mListener.onGetSuggestionsForSentence(
                        mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit));
            } catch (RemoteException e) {
            }
        }

        @Override
        public void onCancel() {
            mSession.onCancel();
+69 −1
Original line number Diff line number Diff line
@@ -88,14 +88,17 @@ public class SpellCheckerSession {
     * This meta-data must reference an XML resource.
     **/
    public static final String SERVICE_META_DATA = "android.view.textservice.scs";
    private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck";


    private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
    private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;

    private final InternalListener mInternalListener;
    private final ITextServicesManager mTextServicesManager;
    private final SpellCheckerInfo mSpellCheckerInfo;
    private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
    private final SpellCheckerSubtype mSubtype;

    private boolean mIsUsed;
    private SpellCheckerSessionListener mSpellCheckerSessionListener;
@@ -108,6 +111,9 @@ public class SpellCheckerSession {
                case MSG_ON_GET_SUGGESTION_MULTIPLE:
                    handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
                    break;
                case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
                    handleOnGetSuggestionsMultipleForSentence((SuggestionsInfo[]) msg.obj);
                    break;
            }
        }
    };
@@ -117,7 +123,8 @@ public class SpellCheckerSession {
     * @hide
     */
    public SpellCheckerSession(
            SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) {
            SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener,
            SpellCheckerSubtype subtype) {
        if (info == null || listener == null || tsm == null) {
            throw new NullPointerException();
        }
@@ -127,6 +134,7 @@ public class SpellCheckerSession {
        mTextServicesManager = tsm;
        mIsUsed = true;
        mSpellCheckerSessionListener = listener;
        mSubtype = subtype;
    }

    /**
@@ -166,6 +174,14 @@ public class SpellCheckerSession {
        }
    }

    /**
     * @hide
     */
    public void getSuggestionsForSentence(TextInfo textInfo, int suggestionsLimit) {
        mSpellCheckerSessionListenerImpl.getSuggestionsMultipleForSentence(
                new TextInfo[] {textInfo}, suggestionsLimit);
    }

    /**
     * Get candidate strings for a substring of the specified text.
     * @param textInfo text metadata for a spell checker
@@ -195,10 +211,15 @@ public class SpellCheckerSession {
        mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
    }

    private void handleOnGetSuggestionsMultipleForSentence(SuggestionsInfo[] suggestionInfos) {
        mSpellCheckerSessionListener.onGetSuggestionsForSentence(suggestionInfos);
    }

    private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
        private static final int TASK_CANCEL = 1;
        private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
        private static final int TASK_CLOSE = 3;
        private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
        private final Queue<SpellCheckerParams> mPendingTasks =
                new LinkedList<SpellCheckerParams>();
        private Handler mHandler;
@@ -236,6 +257,9 @@ public class SpellCheckerSession {
                case TASK_CLOSE:
                    processClose();
                    break;
                case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
                    processGetSuggestionsMultipleForSentence(scp);
                    break;
            }
        }

@@ -266,6 +290,15 @@ public class SpellCheckerSession {
                            suggestionsLimit, sequentialWords));
        }

        public void getSuggestionsMultipleForSentence(TextInfo[] textInfos, int suggestionsLimit) {
            if (DBG) {
                Log.w(TAG, "getSuggestionsMultipleForSentence");
            }
            processOrEnqueueTask(
                    new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
                            textInfos, suggestionsLimit, false));
        }

        public void close() {
            if (DBG) {
                Log.w(TAG, "close");
@@ -355,10 +388,34 @@ public class SpellCheckerSession {
            }
        }

        private void processGetSuggestionsMultipleForSentence(SpellCheckerParams scp) {
            if (!checkOpenConnection()) {
                return;
            }
            if (DBG) {
                Log.w(TAG, "Get suggestions from the spell checker.");
            }
            if (scp.mTextInfos.length != 1) {
                throw new IllegalArgumentException();
            }
            try {
                mISpellCheckerSession.onGetSuggestionsMultipleForSentence(
                        scp.mTextInfos, scp.mSuggestionsLimit);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to get suggestions " + e);
            }
        }

        @Override
        public void onGetSuggestions(SuggestionsInfo[] results) {
            mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results));
        }

        @Override
        public void onGetSuggestionsForSentence(SuggestionsInfo[] results) {
            mHandler.sendMessage(
                    Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
        }
    }

    /**
@@ -370,6 +427,10 @@ public class SpellCheckerSession {
         * @param results an array of results of getSuggestions
         */
        public void onGetSuggestions(SuggestionsInfo[] results);
        /**
         * @hide
         */
        public void onGetSuggestionsForSentence(SuggestionsInfo[] results);
    }

    private static class InternalListener extends ITextServicesSessionListener.Stub {
@@ -411,4 +472,11 @@ public class SpellCheckerSession {
    public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
        return mSpellCheckerSessionListenerImpl;
    }

    /**
     * @hide
     */
    public boolean isSentenceSpellCheckSupported() {
        return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK);
    }
}
+48 −0
Original line number Diff line number Diff line
@@ -21,9 +21,11 @@ import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Slog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -33,11 +35,15 @@ import java.util.Locale;
 * Subtype can describe locale (e.g. en_US, fr_FR...) used for settings.
 */
public final class SpellCheckerSubtype implements Parcelable {
    private static final String TAG = SpellCheckerSubtype.class.getSimpleName();
    private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
    private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";

    private final int mSubtypeHashCode;
    private final int mSubtypeNameResId;
    private final String mSubtypeLocale;
    private final String mSubtypeExtraValue;
    private HashMap<String, String> mExtraValueHashMapCache;

    /**
     * Constructor
@@ -83,6 +89,48 @@ public final class SpellCheckerSubtype implements Parcelable {
        return mSubtypeExtraValue;
    }

    private HashMap<String, String> getExtraValueHashMap() {
        if (mExtraValueHashMapCache == null) {
            mExtraValueHashMapCache = new HashMap<String, String>();
            final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
            final int N = pairs.length;
            for (int i = 0; i < N; ++i) {
                final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
                if (pair.length == 1) {
                    mExtraValueHashMapCache.put(pair[0], null);
                } else if (pair.length > 1) {
                    if (pair.length > 2) {
                        Slog.w(TAG, "ExtraValue has two or more '='s");
                    }
                    mExtraValueHashMapCache.put(pair[0], pair[1]);
                }
            }
        }
        return mExtraValueHashMapCache;
    }

    /**
     * @hide
     * The string of ExtraValue in subtype should be defined as follows:
     * example: key0,key1=value1,key2,key3,key4=value4
     * @param key the key of extra value
     * @return the subtype contains specified the extra value
     */
    public boolean containsExtraValueKey(String key) {
        return getExtraValueHashMap().containsKey(key);
    }

    /**
     * @hide
     * The string of ExtraValue in subtype should be defined as follows:
     * example: key0,key1=value1,key2,key3,key4=value4
     * @param key the key of extra value
     * @return the value of the specified key
     */
    public String getExtraValueOf(String key) {
        return getExtraValueHashMap().get(key);
    }

    @Override
    public int hashCode() {
        return mSubtypeHashCode;
+66 −10
Original line number Diff line number Diff line
@@ -21,11 +21,14 @@ import com.android.internal.util.ArrayUtils;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.Arrays;

/**
 * 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);
    private static final int NOT_A_LENGTH = -1;

    /**
     * Flag of the attributes of the suggestions that can be obtained by
@@ -47,6 +50,8 @@ public final class SuggestionsInfo implements Parcelable {
    public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004;
    private final int mSuggestionsAttributes;
    private final String[] mSuggestions;
    private final int[] mStartPosArray;
    private final int[] mLengthArray;
    private final boolean mSuggestionsAvailable;
    private int mCookie;
    private int mSequence;
@@ -57,16 +62,7 @@ public final class SuggestionsInfo implements Parcelable {
     * @param suggestions from the text service
     */
    public SuggestionsInfo(int suggestionsAttributes, String[] suggestions) {
        mSuggestionsAttributes = suggestionsAttributes;
        if (suggestions == null) {
            mSuggestions = EMPTY;
            mSuggestionsAvailable = false;
        } else {
            mSuggestions = suggestions;
            mSuggestionsAvailable = true;
        }
        mCookie = 0;
        mSequence = 0;
        this(suggestionsAttributes, suggestions, 0, 0);
    }

    /**
@@ -78,12 +74,46 @@ public final class SuggestionsInfo implements Parcelable {
     */
    public SuggestionsInfo(
            int suggestionsAttributes, String[] suggestions, int cookie, int sequence) {
        this(suggestionsAttributes, suggestions, cookie, sequence, null, null);
    }

    /**
     * @hide
     * Constructor.
     * @param suggestionsAttributes from the text service
     * @param suggestions from the text service
     * @param cookie the cookie of the input TextInfo
     * @param sequence the cookie of the input TextInfo
     * @param startPosArray the array of start positions of suggestions
     * @param lengthArray the array of length of suggestions
     */
    public SuggestionsInfo(
            int suggestionsAttributes, String[] suggestions, int cookie, int sequence,
            int[] startPosArray, int[] lengthArray) {
        final int suggestsLen;
        if (suggestions == null) {
            mSuggestions = EMPTY;
            mSuggestionsAvailable = false;
            suggestsLen = 0;
            mStartPosArray = new int[0];
            mLengthArray = new int[0];
        } else {
            mSuggestions = suggestions;
            mSuggestionsAvailable = true;
            suggestsLen = suggestions.length;
            if (startPosArray == null || lengthArray == null) {
                mStartPosArray = new int[suggestsLen];
                mLengthArray = new int[suggestsLen];
                for (int i = 0; i < suggestsLen; ++i) {
                    mStartPosArray[i] = 0;
                    mLengthArray[i] = NOT_A_LENGTH;
                }
            } else if (suggestsLen != startPosArray.length || suggestsLen != lengthArray.length) {
                throw new IllegalArgumentException();
            } else {
                mStartPosArray = Arrays.copyOf(startPosArray, suggestsLen);
                mLengthArray = Arrays.copyOf(lengthArray, suggestsLen);
            }
        }
        mSuggestionsAttributes = suggestionsAttributes;
        mCookie = cookie;
@@ -96,6 +126,10 @@ public final class SuggestionsInfo implements Parcelable {
        mCookie = source.readInt();
        mSequence = source.readInt();
        mSuggestionsAvailable = source.readInt() == 1;
        mStartPosArray = new int[mSuggestions.length];
        mLengthArray = new int[mSuggestions.length];
        source.readIntArray(mStartPosArray);
        source.readIntArray(mLengthArray);
    }

    /**
@@ -111,6 +145,8 @@ public final class SuggestionsInfo implements Parcelable {
        dest.writeInt(mCookie);
        dest.writeInt(mSequence);
        dest.writeInt(mSuggestionsAvailable ? 1 : 0);
        dest.writeIntArray(mStartPosArray);
        dest.writeIntArray(mLengthArray);
    }

    /**
@@ -191,4 +227,24 @@ public final class SuggestionsInfo implements Parcelable {
    public int describeContents() {
        return 0;
    }

    /**
     * @hide
     */
    public int getSuggestionStartPosAt(int i) {
        if (i >= 0 && i < mStartPosArray.length) {
            return mStartPosArray[i];
        }
        return -1;
    }

    /**
     * @hide
     */
    public int getSuggestionLengthAt(int i) {
        if (i >= 0 && i < mLengthArray.length) {
            return mLengthArray[i];
        }
        return -1;
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -157,7 +157,8 @@ public final class TextServicesManager {
        if (subtypeInUse == null) {
            return null;
        }
        final SpellCheckerSession session = new SpellCheckerSession(sci, sService, listener);
        final SpellCheckerSession session = new SpellCheckerSession(
                sci, sService, listener, subtypeInUse);
        try {
            sService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
                    session.getTextServicesSessionListener(),
Loading