SpellCheckerSession.java revision b5052de75736527549d7e537632777c6fec2e4f0
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";
940dc1f648a09b46c45190ba1ce7daecf7fada4347satok    private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck";
95aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok
96988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
97988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
980dc1f648a09b46c45190ba1ce7daecf7fada4347satok    private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
99988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
100988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final InternalListener mInternalListener;
101988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final ITextServicesManager mTextServicesManager;
1021bedd99761e3d2acdac947d641e7fee5db556141satok    private final SpellCheckerInfo mSpellCheckerInfo;
103988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
1040dc1f648a09b46c45190ba1ce7daecf7fada4347satok    private final SpellCheckerSubtype mSubtype;
105988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
106988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private boolean mIsUsed;
107988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private SpellCheckerSessionListener mSpellCheckerSessionListener;
108988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
109988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /** Handler that will execute the main tasks */
110988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final Handler mHandler = new Handler() {
111988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
112988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void handleMessage(Message msg) {
113988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            switch (msg.what) {
114988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                case MSG_ON_GET_SUGGESTION_MULTIPLE:
115988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
116988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    break;
1170dc1f648a09b46c45190ba1ce7daecf7fada4347satok                case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
1180dc1f648a09b46c45190ba1ce7daecf7fada4347satok                    handleOnGetSuggestionsMultipleForSentence((SuggestionsInfo[]) msg.obj);
1190dc1f648a09b46c45190ba1ce7daecf7fada4347satok                    break;
120988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
121988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
122988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    };
123988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
124988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
125988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * Constructor
126988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @hide
127988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
1281bedd99761e3d2acdac947d641e7fee5db556141satok    public SpellCheckerSession(
1290dc1f648a09b46c45190ba1ce7daecf7fada4347satok            SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener,
1300dc1f648a09b46c45190ba1ce7daecf7fada4347satok            SpellCheckerSubtype subtype) {
1311bedd99761e3d2acdac947d641e7fee5db556141satok        if (info == null || listener == null || tsm == null) {
132988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            throw new NullPointerException();
133988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
1341bedd99761e3d2acdac947d641e7fee5db556141satok        mSpellCheckerInfo = info;
135988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler);
136a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        mInternalListener = new InternalListener(mSpellCheckerSessionListenerImpl);
137988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mTextServicesManager = tsm;
138988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mIsUsed = true;
139988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListener = listener;
1400dc1f648a09b46c45190ba1ce7daecf7fada4347satok        mSubtype = subtype;
141988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
142988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
143988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
144988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @return true if the connection to a text service of this session is disconnected and not
145988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * alive.
146988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
147988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public boolean isSessionDisconnected() {
148988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        return mSpellCheckerSessionListenerImpl.isDisconnected();
149988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
150988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
151988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
1521bedd99761e3d2acdac947d641e7fee5db556141satok     * Get the spell checker service info this spell checker session has.
1531bedd99761e3d2acdac947d641e7fee5db556141satok     * @return SpellCheckerInfo for the specified locale.
1541bedd99761e3d2acdac947d641e7fee5db556141satok     */
1551bedd99761e3d2acdac947d641e7fee5db556141satok    public SpellCheckerInfo getSpellChecker() {
1561bedd99761e3d2acdac947d641e7fee5db556141satok        return mSpellCheckerInfo;
1571bedd99761e3d2acdac947d641e7fee5db556141satok    }
1581bedd99761e3d2acdac947d641e7fee5db556141satok
1591bedd99761e3d2acdac947d641e7fee5db556141satok    /**
160b4aff97c85e730857893742f73a082f6b8d139casatok     * Cancel pending and running spell check tasks
161b4aff97c85e730857893742f73a082f6b8d139casatok     */
162b4aff97c85e730857893742f73a082f6b8d139casatok    public void cancel() {
163b4aff97c85e730857893742f73a082f6b8d139casatok        mSpellCheckerSessionListenerImpl.cancel();
164b4aff97c85e730857893742f73a082f6b8d139casatok    }
165b4aff97c85e730857893742f73a082f6b8d139casatok
166b4aff97c85e730857893742f73a082f6b8d139casatok    /**
167988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * Finish this session and allow TextServicesManagerService to disconnect the bound spell
168988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * checker.
169988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
170988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public void close() {
171988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mIsUsed = false;
172988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        try {
17374061ff90453c79ddbde468f630a41425da07710satok            mSpellCheckerSessionListenerImpl.close();
174988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl);
175988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        } catch (RemoteException e) {
176988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            // do nothing
177988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
178988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
179988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
180988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
1810dc1f648a09b46c45190ba1ce7daecf7fada4347satok     * @hide
1820dc1f648a09b46c45190ba1ce7daecf7fada4347satok     */
1830dc1f648a09b46c45190ba1ce7daecf7fada4347satok    public void getSuggestionsForSentence(TextInfo textInfo, int suggestionsLimit) {
1840dc1f648a09b46c45190ba1ce7daecf7fada4347satok        mSpellCheckerSessionListenerImpl.getSuggestionsMultipleForSentence(
1850dc1f648a09b46c45190ba1ce7daecf7fada4347satok                new TextInfo[] {textInfo}, suggestionsLimit);
1860dc1f648a09b46c45190ba1ce7daecf7fada4347satok    }
1870dc1f648a09b46c45190ba1ce7daecf7fada4347satok
1880dc1f648a09b46c45190ba1ce7daecf7fada4347satok    /**
189988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * Get candidate strings for a substring of the specified text.
190988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param textInfo text metadata for a spell checker
191988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param suggestionsLimit the number of limit of suggestions returned
192988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
193988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
194988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false);
195988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
196988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
197988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
198988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * A batch process of getSuggestions
199988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param textInfos an array of text metadata for a spell checker
200988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param suggestionsLimit the number of limit of suggestions returned
201988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @param sequentialWords true if textInfos can be treated as sequential words.
202988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
203988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public void getSuggestions(
204988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
2056be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        if (DBG) {
2066be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId());
2076be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        }
208988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        // TODO: Handle multiple words suggestions by using WordBreakIterator
209988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
210988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                textInfos, suggestionsLimit, sequentialWords);
211988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
212988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
213988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) {
214988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
215988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
216988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
2170dc1f648a09b46c45190ba1ce7daecf7fada4347satok    private void handleOnGetSuggestionsMultipleForSentence(SuggestionsInfo[] suggestionInfos) {
2180dc1f648a09b46c45190ba1ce7daecf7fada4347satok        mSpellCheckerSessionListener.onGetSuggestionsForSentence(suggestionInfos);
2190dc1f648a09b46c45190ba1ce7daecf7fada4347satok    }
2200dc1f648a09b46c45190ba1ce7daecf7fada4347satok
221988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
222988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private static final int TASK_CANCEL = 1;
223988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
22474061ff90453c79ddbde468f630a41425da07710satok        private static final int TASK_CLOSE = 3;
2250dc1f648a09b46c45190ba1ce7daecf7fada4347satok        private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
226988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private final Queue<SpellCheckerParams> mPendingTasks =
227988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                new LinkedList<SpellCheckerParams>();
228060677f4686a93d92117d7d472e754423a368bdbsatok        private Handler mHandler;
229988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
230988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private boolean mOpened;
231988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private ISpellCheckerSession mISpellCheckerSession;
23233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn        private HandlerThread mThread;
23333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn        private Handler mAsyncHandler;
234988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
235988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public SpellCheckerSessionListenerImpl(Handler handler) {
236988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mOpened = false;
237988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mHandler = handler;
238988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
239988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
240988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private static class SpellCheckerParams {
241988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final int mWhat;
242988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final TextInfo[] mTextInfos;
243988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final int mSuggestionsLimit;
244988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public final boolean mSequentialWords;
24533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            public ISpellCheckerSession mSession;
246988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit,
247988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    boolean sequentialWords) {
248988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mWhat = what;
249988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mTextInfos = textInfos;
250988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mSuggestionsLimit = suggestionsLimit;
251988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mSequentialWords = sequentialWords;
252988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
253988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
254988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
25533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn        private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
25633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                boolean async) {
25733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn            if (async || mAsyncHandler == null) {
25833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                switch (scp.mWhat) {
25933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    case TASK_CANCEL:
26033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        if (DBG) {
26133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.w(TAG, "Cancel spell checker tasks.");
26233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
26333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        try {
26433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            session.onCancel();
26533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        } catch (RemoteException e) {
26633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.e(TAG, "Failed to cancel " + e);
26733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
26833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        break;
26933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                    case TASK_GET_SUGGESTIONS_MULTIPLE:
27033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        if (DBG) {
27133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.w(TAG, "Get suggestions from the spell checker.");
27233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
27333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        try {
27433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            session.onGetSuggestionsMultiple(scp.mTextInfos,
27533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                                    scp.mSuggestionsLimit, scp.mSequentialWords);
27633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        } catch (RemoteException e) {
27733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                            Log.e(TAG, "Failed to get suggestions " + e);
27833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        }
27933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                        break;
280b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                    case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
281b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        if (DBG) {
282b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                            Log.w(TAG, "Get suggestions from the spell checker.");
283b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        }
284b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        if (scp.mTextInfos.length != 1) {
285b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                            throw new IllegalArgumentException();
286b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        }
287b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                        try {
288b5052de75736527549d7e537632777c6fec2e4f0Dianne Hackborn                            session.onGetSuggestionsMultipleForSentence(
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
3690dc1f648a09b46c45190ba1ce7daecf7fada4347satok        public void getSuggestionsMultipleForSentence(TextInfo[] textInfos, int suggestionsLimit) {
3700dc1f648a09b46c45190ba1ce7daecf7fada4347satok            if (DBG) {
3710dc1f648a09b46c45190ba1ce7daecf7fada4347satok                Log.w(TAG, "getSuggestionsMultipleForSentence");
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) {
40233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                                // Only one close task should be processed, while we need to remove all
40333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn                                // 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
4290dc1f648a09b46c45190ba1ce7daecf7fada4347satok        public void onGetSuggestionsForSentence(SuggestionsInfo[] 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        /**
440988323c57bd25a58f05dfa492d9b9c8ab62c5153satok         * Callback for "getSuggestions"
441988323c57bd25a58f05dfa492d9b9c8ab62c5153satok         * @param results an array of results of getSuggestions
442988323c57bd25a58f05dfa492d9b9c8ab62c5153satok         */
443988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onGetSuggestions(SuggestionsInfo[] results);
4440dc1f648a09b46c45190ba1ce7daecf7fada4347satok        /**
4450dc1f648a09b46c45190ba1ce7daecf7fada4347satok         * @hide
4460dc1f648a09b46c45190ba1ce7daecf7fada4347satok         */
4470dc1f648a09b46c45190ba1ce7daecf7fada4347satok        public void onGetSuggestionsForSentence(SuggestionsInfo[] results);
448988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
449988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
450a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard    private static class InternalListener extends ITextServicesSessionListener.Stub {
451a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        private final SpellCheckerSessionListenerImpl mParentSpellCheckerSessionListenerImpl;
452a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard
453a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        public InternalListener(SpellCheckerSessionListenerImpl spellCheckerSessionListenerImpl) {
454a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard            mParentSpellCheckerSessionListenerImpl = spellCheckerSessionListenerImpl;
455a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard        }
456a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard
457988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
458988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onServiceConnected(ISpellCheckerSession session) {
4596be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            if (DBG) {
4606be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok                Log.w(TAG, "SpellCheckerSession connected.");
4616be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            }
462a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard            mParentSpellCheckerSessionListenerImpl.onServiceConnected(session);
463988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
464988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
465988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
466988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    @Override
467988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    protected void finalize() throws Throwable {
468988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        super.finalize();
469988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        if (mIsUsed) {
470988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            Log.e(TAG, "SpellCheckerSession was not finished properly." +
471988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    "You should call finishShession() when you finished to use a spell checker.");
472988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            close();
473988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
474988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
475988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
476988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
477988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @hide
478988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
479988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public ITextServicesSessionListener getTextServicesSessionListener() {
480988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        return mInternalListener;
481988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
482988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
483988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
484988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     * @hide
485988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
486988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
487988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        return mSpellCheckerSessionListenerImpl;
488988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
4890dc1f648a09b46c45190ba1ce7daecf7fada4347satok
4900dc1f648a09b46c45190ba1ce7daecf7fada4347satok    /**
4910dc1f648a09b46c45190ba1ce7daecf7fada4347satok     * @hide
4920dc1f648a09b46c45190ba1ce7daecf7fada4347satok     */
4930dc1f648a09b46c45190ba1ce7daecf7fada4347satok    public boolean isSentenceSpellCheckSupported() {
4940dc1f648a09b46c45190ba1ce7daecf7fada4347satok        return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK);
4950dc1f648a09b46c45190ba1ce7daecf7fada4347satok    }
496988323c57bd25a58f05dfa492d9b9c8ab62c5153satok}
497