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

Commit 0dc1f648 authored by satok's avatar satok
Browse files

add sentence level spell checker hidden apis

Change-Id: If65c89044bed064fd01a554e33176f64f7c21c0f
parent fbd85491
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