1988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/*
2988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Copyright (C) 2011 The Android Open Source Project
3988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
4988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Licensed under the Apache License, Version 2.0 (the "License");
5988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * you may not use this file except in compliance with the License.
6988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * You may obtain a copy of the License at
7988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
8988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *      http://www.apache.org/licenses/LICENSE-2.0
9988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
10988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Unless required by applicable law or agreed to in writing, software
11988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * distributed under the License is distributed on an "AS IS" BASIS,
12988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * See the License for the specific language governing permissions and
14988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * limitations under the License.
15988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */
16988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
17aafd955fa8f5c31c511763c0f826b6d7acf15b9csatokpackage android.view.textservice;
18988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
19988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSession;
20988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSessionListener;
21988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesManager;
22988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesSessionListener;
23988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
2433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackbornimport android.os.Binder;
25988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.Handler;
2633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackbornimport android.os.HandlerThread;
27988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.Message;
2833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackbornimport android.os.Process;
29988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.RemoteException;
30988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.util.Log;
311bedd99761e3d2acdac947d641e7fee5db556141satokimport android.view.textservice.SpellCheckerInfo;
32988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.SuggestionsInfo;
33988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.TextInfo;
34988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
35988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.LinkedList;
36988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.Queue;
37988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
38988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/**
39988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * The SpellCheckerSession interface provides the per client functionality of SpellCheckerService.
4044b75030931d9c65c9e495a86d11d71da59b4429satok *
4144b75030931d9c65c9e495a86d11d71da59b4429satok *
4244b75030931d9c65c9e495a86d11d71da59b4429satok * <a name="Applications"></a>
4344b75030931d9c65c9e495a86d11d71da59b4429satok * <h3>Applications</h3>
4444b75030931d9c65c9e495a86d11d71da59b4429satok *
4544b75030931d9c65c9e495a86d11d71da59b4429satok * <p>In most cases, applications that are using the standard
4644b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.widget.TextView} or its subclasses will have little they need
4744b75030931d9c65c9e495a86d11d71da59b4429satok * to do to work well with spell checker services.  The main things you need to
4844b75030931d9c65c9e495a86d11d71da59b4429satok * be aware of are:</p>
4944b75030931d9c65c9e495a86d11d71da59b4429satok *
5044b75030931d9c65c9e495a86d11d71da59b4429satok * <ul>
5144b75030931d9c65c9e495a86d11d71da59b4429satok * <li> Properly set the {@link android.R.attr#inputType} in your editable
5244b75030931d9c65c9e495a86d11d71da59b4429satok * text views, so that the spell checker will have enough context to help the
5344b75030931d9c65c9e495a86d11d71da59b4429satok * user in editing text in them.
5444b75030931d9c65c9e495a86d11d71da59b4429satok * </ul>
5544b75030931d9c65c9e495a86d11d71da59b4429satok *
5644b75030931d9c65c9e495a86d11d71da59b4429satok * <p>For the rare people amongst us writing client applications that use the spell checker service
5744b75030931d9c65c9e495a86d11d71da59b4429satok * directly, you will need to use {@link #getSuggestions(TextInfo, int)} or
5844b75030931d9c65c9e495a86d11d71da59b4429satok * {@link #getSuggestions(TextInfo[], int, boolean)} for obtaining results from the spell checker
5944b75030931d9c65c9e495a86d11d71da59b4429satok * service by yourself.</p>
6044b75030931d9c65c9e495a86d11d71da59b4429satok *
6144b75030931d9c65c9e495a86d11d71da59b4429satok * <h3>Security</h3>
6244b75030931d9c65c9e495a86d11d71da59b4429satok *
6344b75030931d9c65c9e495a86d11d71da59b4429satok * <p>There are a lot of security issues associated with spell checkers,
6444b75030931d9c65c9e495a86d11d71da59b4429satok * since they could monitor all the text being sent to them
6544b75030931d9c65c9e495a86d11d71da59b4429satok * through, for instance, {@link android.widget.TextView}.
6644b75030931d9c65c9e495a86d11d71da59b4429satok * The Android spell checker framework also allows
6744b75030931d9c65c9e495a86d11d71da59b4429satok * arbitrary third party spell checkers, so care must be taken to restrict their
6844b75030931d9c65c9e495a86d11d71da59b4429satok * selection and interactions.</p>
6944b75030931d9c65c9e495a86d11d71da59b4429satok *
7044b75030931d9c65c9e495a86d11d71da59b4429satok * <p>Here are some key points about the security architecture behind the
7144b75030931d9c65c9e495a86d11d71da59b4429satok * spell checker framework:</p>
7244b75030931d9c65c9e495a86d11d71da59b4429satok *
7344b75030931d9c65c9e495a86d11d71da59b4429satok * <ul>
7444b75030931d9c65c9e495a86d11d71da59b4429satok * <li>Only the system is allowed to directly access a spell checker framework's
7544b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.service.textservice.SpellCheckerService} interface, via the
7644b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.Manifest.permission#BIND_TEXT_SERVICE} permission.  This is
7744b75030931d9c65c9e495a86d11d71da59b4429satok * enforced in the system by not binding to a spell checker service that does
7844b75030931d9c65c9e495a86d11d71da59b4429satok * not require this permission.
7944b75030931d9c65c9e495a86d11d71da59b4429satok *
8044b75030931d9c65c9e495a86d11d71da59b4429satok * <li>The user must explicitly enable a new spell checker in settings before
8144b75030931d9c65c9e495a86d11d71da59b4429satok * they can be enabled, to confirm with the system that they know about it
8244b75030931d9c65c9e495a86d11d71da59b4429satok * and want to make it available for use.
8344b75030931d9c65c9e495a86d11d71da59b4429satok * </ul>
8444b75030931d9c65c9e495a86d11d71da59b4429satok *
85988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */
86988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpublic class SpellCheckerSession {
87988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static final String TAG = SpellCheckerSession.class.getSimpleName();
88988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static final boolean DBG = false;
89aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok    /**
90aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok     * Name under which a SpellChecker service component publishes information about itself.
91aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok     * This meta-data must reference an XML resource.
92aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok     **/
93aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok    public static final String SERVICE_META_DATA = "android.view.textservice.scs";
94988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
95988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
960dc1f648a09b46c45190ba1ce7daecf7fada4347satok    private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
97988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
98988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final InternalListener mInternalListener;
99988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final ITextServicesManager mTextServicesManager;
1001bedd99761e3d2acdac947d641e7fee5db556141satok    private final SpellCheckerInfo mSpellCheckerInfo;
101988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
1020dc1f648a09b46c45190ba1ce7daecf7fada4347satok    private final SpellCheckerSubtype mSubtype;
103988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
104988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private boolean mIsUsed;
105988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private SpellCheckerSessionListener mSpellCheckerSessionListener;
106988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
107988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /** Handler that will execute the main tasks */
108988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final Handler mHandler = new Handler() {
109988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
110988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void handleMessage(Message msg) {
111988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            switch (msg.what) {
112988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                case MSG_ON_GET_SUGGESTION_MULTIPLE:
113988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
114988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    break;
1150dc1f648a09b46c45190ba1ce7daecf7fada4347satok                case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
116d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok                    handleOnGetSentenceSuggestionsMultiple((SentenceSuggestionsInfo[]) msg.obj);
1170dc1f648a09b46c45190ba1ce7daecf7fada4347satok                    break;
118988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
119988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
120988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    };
121988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
122988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
123988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * Constructor
124988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @hide
125988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
1261bedd99761e3d2acdac947d641e7fee5db556141satok    public SpellCheckerSession(
1270dc1f648a09b46c45190ba1ce7daecf7fada4347satok            SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener,
1280dc1f648a09b46c45190ba1ce7daecf7fada4347satok            SpellCheckerSubtype subtype) {
1291bedd99761e3d2acdac947d641e7fee5db556141satok        if (info == null || listener == null || tsm == null) {
130988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            throw new NullPointerException();
131988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
1321bedd99761e3d2acdac947d641e7fee5db556141satok        mSpellCheckerInfo = info;
133988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler);
134a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        mInternalListener = new InternalListener(mSpellCheckerSessionListenerImpl);
135988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mTextServicesManager = tsm;
136988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mIsUsed = true;
137988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListener = listener;
1380dc1f648a09b46c45190ba1ce7daecf7fada4347satok        mSubtype = subtype;
139988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
140988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
141988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
142988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @return true if the connection to a text service of this session is disconnected and not
143988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * alive.
144988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
145988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public boolean isSessionDisconnected() {
146988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        return mSpellCheckerSessionListenerImpl.isDisconnected();
147988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
148988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
149988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
1501bedd99761e3d2acdac947d641e7fee5db556141satok     * Get the spell checker service info this spell checker session has.
1511bedd99761e3d2acdac947d641e7fee5db556141satok     * @return SpellCheckerInfo for the specified locale.
1521bedd99761e3d2acdac947d641e7fee5db556141satok     */
1531bedd99761e3d2acdac947d641e7fee5db556141satok    public SpellCheckerInfo getSpellChecker() {
1541bedd99761e3d2acdac947d641e7fee5db556141satok        return mSpellCheckerInfo;
1551bedd99761e3d2acdac947d641e7fee5db556141satok    }
1561bedd99761e3d2acdac947d641e7fee5db556141satok
1571bedd99761e3d2acdac947d641e7fee5db556141satok    /**
158b4aff97c85e730857893742f73a082f6b8d139casatok     * Cancel pending and running spell check tasks
159b4aff97c85e730857893742f73a082f6b8d139casatok     */
160b4aff97c85e730857893742f73a082f6b8d139casatok    public void cancel() {
161b4aff97c85e730857893742f73a082f6b8d139casatok        mSpellCheckerSessionListenerImpl.cancel();
162b4aff97c85e730857893742f73a082f6b8d139casatok    }
163b4aff97c85e730857893742f73a082f6b8d139casatok
164b4aff97c85e730857893742f73a082f6b8d139casatok    /**
165988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * Finish this session and allow TextServicesManagerService to disconnect the bound spell
166988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * checker.
167988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
168988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public void close() {
169988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mIsUsed = false;
170988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        try {
17174061ff90453c79ddbde468f630a41425da07710satok            mSpellCheckerSessionListenerImpl.close();
172988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl);
173988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        } catch (RemoteException e) {
174988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            // do nothing
175988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
176988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
177988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
178988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
1796183cd64a98a69ea247813c9ba0a07326c4bc1aesatok     * Get suggestions from the specified sentences
1806183cd64a98a69ea247813c9ba0a07326c4bc1aesatok     * @param textInfos an array of text metadata for a spell checker
1816183cd64a98a69ea247813c9ba0a07326c4bc1aesatok     * @param suggestionsLimit the maximum number of suggestions that will be returned
1820dc1f648a09b46c45190ba1ce7daecf7fada4347satok     */
1836183cd64a98a69ea247813c9ba0a07326c4bc1aesatok    public void getSentenceSuggestions(TextInfo[] textInfos, int suggestionsLimit) {
184d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok        mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
1856183cd64a98a69ea247813c9ba0a07326c4bc1aesatok                textInfos, suggestionsLimit);
1860dc1f648a09b46c45190ba1ce7daecf7fada4347satok    }
1870dc1f648a09b46c45190ba1ce7daecf7fada4347satok
1880dc1f648a09b46c45190ba1ce7daecf7fada4347satok    /**
189988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * Get candidate strings for a substring of the specified text.
190988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param textInfo text metadata for a spell checker
1916183cd64a98a69ea247813c9ba0a07326c4bc1aesatok     * @param suggestionsLimit the maximum number of suggestions that will be returned
192c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok     * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
193988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
194c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok    @Deprecated
195988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
196988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false);
197988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
198988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
199988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
200988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * A batch process of getSuggestions
201988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param textInfos an array of text metadata for a spell checker
2026183cd64a98a69ea247813c9ba0a07326c4bc1aesatok     * @param suggestionsLimit the maximum number of suggestions that will be returned
203988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param sequentialWords true if textInfos can be treated as sequential words.
204c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok     * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
205988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
206c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok    @Deprecated
207988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public void getSuggestions(
208988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
2096be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        if (DBG) {
2106be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId());
2116be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        }
212988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
213988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                textInfos, suggestionsLimit, sequentialWords);
214988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
215988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
216988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) {
217988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
218988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
219988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
220d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok    private void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionInfos) {
221d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok        mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionInfos);
2220dc1f648a09b46c45190ba1ce7daecf7fada4347satok    }
2230dc1f648a09b46c45190ba1ce7daecf7fada4347satok
224988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
225988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private static final int TASK_CANCEL = 1;
226988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
22774061ff90453c79ddbde468f630a41425da07710satok        private static final int TASK_CLOSE = 3;
2280dc1f648a09b46c45190ba1ce7daecf7fada4347satok        private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
229988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private final Queue<SpellCheckerParams> mPendingTasks =
230988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                new LinkedList<SpellCheckerParams>();
231060677f4686a93d92117d7d472e754423a368bdbsatok        private Handler mHandler;
232988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
233988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private boolean mOpened;
234988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private ISpellCheckerSession mISpellCheckerSession;
23533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn        private HandlerThread mThread;
23633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn        private Handler mAsyncHandler;
237988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
238988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public SpellCheckerSessionListenerImpl(Handler handler) {
239988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mOpened = false;
240988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mHandler = handler;
241988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
242988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
243988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private static class SpellCheckerParams {
244988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final int mWhat;
245988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final TextInfo[] mTextInfos;
246988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final int mSuggestionsLimit;
247988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final boolean mSequentialWords;
24833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            public ISpellCheckerSession mSession;
249988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit,
250988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    boolean sequentialWords) {
251988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mWhat = what;
252988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mTextInfos = textInfos;
253988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mSuggestionsLimit = suggestionsLimit;
254988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mSequentialWords = sequentialWords;
255988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
256988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
257988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
25833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn        private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
25933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                boolean async) {
26033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            if (async || mAsyncHandler == null) {
26133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                switch (scp.mWhat) {
26233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    case TASK_CANCEL:
26333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        if (DBG) {
26433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.w(TAG, "Cancel spell checker tasks.");
26533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
26633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        try {
26733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            session.onCancel();
26833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        } catch (RemoteException e) {
26933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.e(TAG, "Failed to cancel " + e);
27033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
27133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        break;
27233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    case TASK_GET_SUGGESTIONS_MULTIPLE:
27333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        if (DBG) {
27433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.w(TAG, "Get suggestions from the spell checker.");
27533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
27633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        try {
27733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            session.onGetSuggestionsMultiple(scp.mTextInfos,
27833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                                    scp.mSuggestionsLimit, scp.mSequentialWords);
27933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        } catch (RemoteException e) {
28033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.e(TAG, "Failed to get suggestions " + e);
28133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
28233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        break;
283b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                    case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
284b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        if (DBG) {
285c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok                            Log.w(TAG, "Get sentence suggestions from the spell checker.");
286b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        }
287b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        try {
288d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok                            session.onGetSentenceSuggestionsMultiple(
289b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                                    scp.mTextInfos, scp.mSuggestionsLimit);
290b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        } catch (RemoteException e) {
291b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                            Log.e(TAG, "Failed to get suggestions " + e);
292b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        }
293b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        break;
29433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    case TASK_CLOSE:
29533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        if (DBG) {
29633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.w(TAG, "Close spell checker tasks.");
29733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
29833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        try {
29933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            session.onClose();
30033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        } catch (RemoteException e) {
30133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.e(TAG, "Failed to close " + e);
30233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
30333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        break;
30433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                }
30533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            } else {
30633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                // The interface is to a local object, so need to execute it
30733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                // asynchronously.
30833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                scp.mSession = session;
30933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                mAsyncHandler.sendMessage(Message.obtain(mAsyncHandler, 1, scp));
31033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            }
31133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn
31233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            if (scp.mWhat == TASK_CLOSE) {
31333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                // If we are closing, we want to clean up our state now even
31433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                // if it is pending as an async operation.
31533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                synchronized (this) {
31633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mISpellCheckerSession = null;
31733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mHandler = null;
31833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    if (mThread != null) {
31933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        mThread.quit();
32033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    }
32133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mThread = null;
32233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mAsyncHandler = null;
32333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                }
324988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
325988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
326988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
327988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public synchronized void onServiceConnected(ISpellCheckerSession session) {
32833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            synchronized (this) {
32933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                mISpellCheckerSession = session;
33033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                if (session.asBinder() instanceof Binder && mThread == null) {
33133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    // If this is a local object, we need to do our own threading
33233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    // to make sure we handle it asynchronously.
33333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mThread = new HandlerThread("SpellCheckerSession",
33433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Process.THREAD_PRIORITY_BACKGROUND);
33533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mThread.start();
33633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mAsyncHandler = new Handler(mThread.getLooper()) {
33733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        @Override public void handleMessage(Message msg) {
33833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            SpellCheckerParams scp = (SpellCheckerParams)msg.obj;
33933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            processTask(scp.mSession, scp, true);
34033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
34133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    };
34233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                }
34333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                mOpened = true;
34433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            }
345988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            if (DBG)
346988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                Log.d(TAG, "onServiceConnected - Success");
347988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            while (!mPendingTasks.isEmpty()) {
34833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                processTask(session, mPendingTasks.poll(), false);
349988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
350988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
351988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
352b4aff97c85e730857893742f73a082f6b8d139casatok        public void cancel() {
353b4aff97c85e730857893742f73a082f6b8d139casatok            if (DBG) {
354b4aff97c85e730857893742f73a082f6b8d139casatok                Log.w(TAG, "cancel");
355b4aff97c85e730857893742f73a082f6b8d139casatok            }
356b4aff97c85e730857893742f73a082f6b8d139casatok            processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false));
357b4aff97c85e730857893742f73a082f6b8d139casatok        }
358b4aff97c85e730857893742f73a082f6b8d139casatok
359988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void getSuggestionsMultiple(
360988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
3616be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            if (DBG) {
3626be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok                Log.w(TAG, "getSuggestionsMultiple");
3636be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            }
364988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            processOrEnqueueTask(
365988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos,
366988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                            suggestionsLimit, sequentialWords));
367988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
368988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
369d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok        public void getSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) {
3700dc1f648a09b46c45190ba1ce7daecf7fada4347satok            if (DBG) {
371d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok                Log.w(TAG, "getSentenceSuggestionsMultiple");
3720dc1f648a09b46c45190ba1ce7daecf7fada4347satok            }
3730dc1f648a09b46c45190ba1ce7daecf7fada4347satok            processOrEnqueueTask(
3740dc1f648a09b46c45190ba1ce7daecf7fada4347satok                    new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
3750dc1f648a09b46c45190ba1ce7daecf7fada4347satok                            textInfos, suggestionsLimit, false));
3760dc1f648a09b46c45190ba1ce7daecf7fada4347satok        }
3770dc1f648a09b46c45190ba1ce7daecf7fada4347satok
37874061ff90453c79ddbde468f630a41425da07710satok        public void close() {
37974061ff90453c79ddbde468f630a41425da07710satok            if (DBG) {
38074061ff90453c79ddbde468f630a41425da07710satok                Log.w(TAG, "close");
38174061ff90453c79ddbde468f630a41425da07710satok            }
38274061ff90453c79ddbde468f630a41425da07710satok            processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false));
38374061ff90453c79ddbde468f630a41425da07710satok        }
38474061ff90453c79ddbde468f630a41425da07710satok
385988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public boolean isDisconnected() {
386988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            return mOpened && mISpellCheckerSession == null;
387988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
388988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
389988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private void processOrEnqueueTask(SpellCheckerParams scp) {
3906be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            if (DBG) {
3916be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok                Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession);
3926be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            }
39333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            ISpellCheckerSession session;
39433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            synchronized (this) {
39533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                session = mISpellCheckerSession;
39633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                if (session == null) {
39733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    SpellCheckerParams closeTask = null;
39833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    if (scp.mWhat == TASK_CANCEL) {
39933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        while (!mPendingTasks.isEmpty()) {
40033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            final SpellCheckerParams tmp = mPendingTasks.poll();
40133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            if (tmp.mWhat == TASK_CLOSE) {
402d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok                                // Only one close task should be processed, while we need to remove
403d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok                                // all close tasks from the queue
40433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                                closeTask = tmp;
40533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            }
406b4aff97c85e730857893742f73a082f6b8d139casatok                        }
407b4aff97c85e730857893742f73a082f6b8d139casatok                    }
40833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mPendingTasks.offer(scp);
40933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    if (closeTask != null) {
41033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        mPendingTasks.offer(closeTask);
41133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    }
41233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    return;
413b4aff97c85e730857893742f73a082f6b8d139casatok                }
4140dc1f648a09b46c45190ba1ce7daecf7fada4347satok            }
41533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            processTask(session, scp, false);
4160dc1f648a09b46c45190ba1ce7daecf7fada4347satok        }
4170dc1f648a09b46c45190ba1ce7daecf7fada4347satok
418988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
419988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onGetSuggestions(SuggestionsInfo[] results) {
42033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            synchronized (this) {
42133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                if (mHandler != null) {
42233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    mHandler.sendMessage(Message.obtain(mHandler,
42333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            MSG_ON_GET_SUGGESTION_MULTIPLE, results));
42433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                }
42533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            }
426988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
4270dc1f648a09b46c45190ba1ce7daecf7fada4347satok
4280dc1f648a09b46c45190ba1ce7daecf7fada4347satok        @Override
429d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok        public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
4300dc1f648a09b46c45190ba1ce7daecf7fada4347satok            mHandler.sendMessage(
4310dc1f648a09b46c45190ba1ce7daecf7fada4347satok                    Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
4320dc1f648a09b46c45190ba1ce7daecf7fada4347satok        }
433988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
434988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
435988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
436988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * Callback for getting results from text services
437988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
438988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public interface SpellCheckerSessionListener {
439988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        /**
440f6710615c6cc1746d1ecc7aebc9afed457dcca41satok         * Callback for {@link SpellCheckerSession#getSuggestions(TextInfo, int)}
441f6710615c6cc1746d1ecc7aebc9afed457dcca41satok         * and {@link SpellCheckerSession#getSuggestions(TextInfo[], int, boolean)}
4426183cd64a98a69ea247813c9ba0a07326c4bc1aesatok         * @param results an array of {@link SuggestionsInfo}s.
4436183cd64a98a69ea247813c9ba0a07326c4bc1aesatok         * These results are suggestions for {@link TextInfo}s queried by
444f6710615c6cc1746d1ecc7aebc9afed457dcca41satok         * {@link SpellCheckerSession#getSuggestions(TextInfo, int)} or
445f6710615c6cc1746d1ecc7aebc9afed457dcca41satok         * {@link SpellCheckerSession#getSuggestions(TextInfo[], int, boolean)}
446988323c57bd25a58f05dfa492d9b9c8ab62c5153satok         */
447988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onGetSuggestions(SuggestionsInfo[] results);
4480dc1f648a09b46c45190ba1ce7daecf7fada4347satok        /**
4496183cd64a98a69ea247813c9ba0a07326c4bc1aesatok         * Callback for {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)}
4506183cd64a98a69ea247813c9ba0a07326c4bc1aesatok         * @param results an array of {@link SentenceSuggestionsInfo}s.
4516183cd64a98a69ea247813c9ba0a07326c4bc1aesatok         * These results are suggestions for {@link TextInfo}s
4526183cd64a98a69ea247813c9ba0a07326c4bc1aesatok         * queried by {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)}.
4530dc1f648a09b46c45190ba1ce7daecf7fada4347satok         */
454d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok        public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results);
455988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
456988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
457a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard    private static class InternalListener extends ITextServicesSessionListener.Stub {
458a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        private final SpellCheckerSessionListenerImpl mParentSpellCheckerSessionListenerImpl;
459a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard
460a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        public InternalListener(SpellCheckerSessionListenerImpl spellCheckerSessionListenerImpl) {
461a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard            mParentSpellCheckerSessionListenerImpl = spellCheckerSessionListenerImpl;
462a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        }
463a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard
464988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
465988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onServiceConnected(ISpellCheckerSession session) {
4666be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            if (DBG) {
4676be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok                Log.w(TAG, "SpellCheckerSession connected.");
4686be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            }
469a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard            mParentSpellCheckerSessionListenerImpl.onServiceConnected(session);
470988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
471988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
472988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
473988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    @Override
474988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    protected void finalize() throws Throwable {
475988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        super.finalize();
476988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        if (mIsUsed) {
477988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            Log.e(TAG, "SpellCheckerSession was not finished properly." +
478988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    "You should call finishShession() when you finished to use a spell checker.");
479988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            close();
480988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
481988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
482988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
483988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
484988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @hide
485988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
486988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public ITextServicesSessionListener getTextServicesSessionListener() {
487988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        return mInternalListener;
488988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
489988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
490988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
491988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @hide
492988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
493988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
494988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        return mSpellCheckerSessionListenerImpl;
495988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
496988323c57bd25a58f05dfa492d9b9c8ab62c5153satok}
497