SpellCheckerSession.java revision 0dc1f648a09b46c45190ba1ce7daecf7fada4347
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 24988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.Handler; 25988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.Message; 26988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.RemoteException; 27988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.util.Log; 281bedd99761e3d2acdac947d641e7fee5db556141satokimport android.view.textservice.SpellCheckerInfo; 29988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.SuggestionsInfo; 30988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.TextInfo; 31988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 32988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.LinkedList; 33988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.Queue; 34988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 35988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/** 36988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * The SpellCheckerSession interface provides the per client functionality of SpellCheckerService. 3744b75030931d9c65c9e495a86d11d71da59b4429satok * 3844b75030931d9c65c9e495a86d11d71da59b4429satok * 3944b75030931d9c65c9e495a86d11d71da59b4429satok * <a name="Applications"></a> 4044b75030931d9c65c9e495a86d11d71da59b4429satok * <h3>Applications</h3> 4144b75030931d9c65c9e495a86d11d71da59b4429satok * 4244b75030931d9c65c9e495a86d11d71da59b4429satok * <p>In most cases, applications that are using the standard 4344b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.widget.TextView} or its subclasses will have little they need 4444b75030931d9c65c9e495a86d11d71da59b4429satok * to do to work well with spell checker services. The main things you need to 4544b75030931d9c65c9e495a86d11d71da59b4429satok * be aware of are:</p> 4644b75030931d9c65c9e495a86d11d71da59b4429satok * 4744b75030931d9c65c9e495a86d11d71da59b4429satok * <ul> 4844b75030931d9c65c9e495a86d11d71da59b4429satok * <li> Properly set the {@link android.R.attr#inputType} in your editable 4944b75030931d9c65c9e495a86d11d71da59b4429satok * text views, so that the spell checker will have enough context to help the 5044b75030931d9c65c9e495a86d11d71da59b4429satok * user in editing text in them. 5144b75030931d9c65c9e495a86d11d71da59b4429satok * </ul> 5244b75030931d9c65c9e495a86d11d71da59b4429satok * 5344b75030931d9c65c9e495a86d11d71da59b4429satok * <p>For the rare people amongst us writing client applications that use the spell checker service 5444b75030931d9c65c9e495a86d11d71da59b4429satok * directly, you will need to use {@link #getSuggestions(TextInfo, int)} or 5544b75030931d9c65c9e495a86d11d71da59b4429satok * {@link #getSuggestions(TextInfo[], int, boolean)} for obtaining results from the spell checker 5644b75030931d9c65c9e495a86d11d71da59b4429satok * service by yourself.</p> 5744b75030931d9c65c9e495a86d11d71da59b4429satok * 5844b75030931d9c65c9e495a86d11d71da59b4429satok * <h3>Security</h3> 5944b75030931d9c65c9e495a86d11d71da59b4429satok * 6044b75030931d9c65c9e495a86d11d71da59b4429satok * <p>There are a lot of security issues associated with spell checkers, 6144b75030931d9c65c9e495a86d11d71da59b4429satok * since they could monitor all the text being sent to them 6244b75030931d9c65c9e495a86d11d71da59b4429satok * through, for instance, {@link android.widget.TextView}. 6344b75030931d9c65c9e495a86d11d71da59b4429satok * The Android spell checker framework also allows 6444b75030931d9c65c9e495a86d11d71da59b4429satok * arbitrary third party spell checkers, so care must be taken to restrict their 6544b75030931d9c65c9e495a86d11d71da59b4429satok * selection and interactions.</p> 6644b75030931d9c65c9e495a86d11d71da59b4429satok * 6744b75030931d9c65c9e495a86d11d71da59b4429satok * <p>Here are some key points about the security architecture behind the 6844b75030931d9c65c9e495a86d11d71da59b4429satok * spell checker framework:</p> 6944b75030931d9c65c9e495a86d11d71da59b4429satok * 7044b75030931d9c65c9e495a86d11d71da59b4429satok * <ul> 7144b75030931d9c65c9e495a86d11d71da59b4429satok * <li>Only the system is allowed to directly access a spell checker framework's 7244b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.service.textservice.SpellCheckerService} interface, via the 7344b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.Manifest.permission#BIND_TEXT_SERVICE} permission. This is 7444b75030931d9c65c9e495a86d11d71da59b4429satok * enforced in the system by not binding to a spell checker service that does 7544b75030931d9c65c9e495a86d11d71da59b4429satok * not require this permission. 7644b75030931d9c65c9e495a86d11d71da59b4429satok * 7744b75030931d9c65c9e495a86d11d71da59b4429satok * <li>The user must explicitly enable a new spell checker in settings before 7844b75030931d9c65c9e495a86d11d71da59b4429satok * they can be enabled, to confirm with the system that they know about it 7944b75030931d9c65c9e495a86d11d71da59b4429satok * and want to make it available for use. 8044b75030931d9c65c9e495a86d11d71da59b4429satok * </ul> 8144b75030931d9c65c9e495a86d11d71da59b4429satok * 82988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 83988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpublic class SpellCheckerSession { 84988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final String TAG = SpellCheckerSession.class.getSimpleName(); 85988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final boolean DBG = false; 86aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok /** 87aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok * Name under which a SpellChecker service component publishes information about itself. 88aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok * This meta-data must reference an XML resource. 89aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok **/ 90aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok public static final String SERVICE_META_DATA = "android.view.textservice.scs"; 910dc1f648a09b46c45190ba1ce7daecf7fada4347satok private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck"; 92aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok 93988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 94988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1; 950dc1f648a09b46c45190ba1ce7daecf7fada4347satok private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2; 96988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 97988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final InternalListener mInternalListener; 98988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final ITextServicesManager mTextServicesManager; 991bedd99761e3d2acdac947d641e7fee5db556141satok private final SpellCheckerInfo mSpellCheckerInfo; 100988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl; 1010dc1f648a09b46c45190ba1ce7daecf7fada4347satok private final SpellCheckerSubtype mSubtype; 102988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 103988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private boolean mIsUsed; 104988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private SpellCheckerSessionListener mSpellCheckerSessionListener; 105988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 106988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** Handler that will execute the main tasks */ 107988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final Handler mHandler = new Handler() { 108988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 109988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void handleMessage(Message msg) { 110988323c57bd25a58f05dfa492d9b9c8ab62c5153satok switch (msg.what) { 111988323c57bd25a58f05dfa492d9b9c8ab62c5153satok case MSG_ON_GET_SUGGESTION_MULTIPLE: 112988323c57bd25a58f05dfa492d9b9c8ab62c5153satok handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj); 113988323c57bd25a58f05dfa492d9b9c8ab62c5153satok break; 1140dc1f648a09b46c45190ba1ce7daecf7fada4347satok case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE: 1150dc1f648a09b46c45190ba1ce7daecf7fada4347satok handleOnGetSuggestionsMultipleForSentence((SuggestionsInfo[]) msg.obj); 1160dc1f648a09b46c45190ba1ce7daecf7fada4347satok break; 117988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 118988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 119988323c57bd25a58f05dfa492d9b9c8ab62c5153satok }; 120988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 121988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 122988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Constructor 123988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @hide 124988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 1251bedd99761e3d2acdac947d641e7fee5db556141satok public SpellCheckerSession( 1260dc1f648a09b46c45190ba1ce7daecf7fada4347satok SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener, 1270dc1f648a09b46c45190ba1ce7daecf7fada4347satok SpellCheckerSubtype subtype) { 1281bedd99761e3d2acdac947d641e7fee5db556141satok if (info == null || listener == null || tsm == null) { 129988323c57bd25a58f05dfa492d9b9c8ab62c5153satok throw new NullPointerException(); 130988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 1311bedd99761e3d2acdac947d641e7fee5db556141satok mSpellCheckerInfo = info; 132988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler); 133a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard mInternalListener = new InternalListener(mSpellCheckerSessionListenerImpl); 134988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTextServicesManager = tsm; 135988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mIsUsed = true; 136988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListener = listener; 1370dc1f648a09b46c45190ba1ce7daecf7fada4347satok mSubtype = subtype; 138988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 139988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 140988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 141988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @return true if the connection to a text service of this session is disconnected and not 142988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * alive. 143988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 144988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean isSessionDisconnected() { 145988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerSessionListenerImpl.isDisconnected(); 146988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 147988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 148988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 1491bedd99761e3d2acdac947d641e7fee5db556141satok * Get the spell checker service info this spell checker session has. 1501bedd99761e3d2acdac947d641e7fee5db556141satok * @return SpellCheckerInfo for the specified locale. 1511bedd99761e3d2acdac947d641e7fee5db556141satok */ 1521bedd99761e3d2acdac947d641e7fee5db556141satok public SpellCheckerInfo getSpellChecker() { 1531bedd99761e3d2acdac947d641e7fee5db556141satok return mSpellCheckerInfo; 1541bedd99761e3d2acdac947d641e7fee5db556141satok } 1551bedd99761e3d2acdac947d641e7fee5db556141satok 1561bedd99761e3d2acdac947d641e7fee5db556141satok /** 157b4aff97c85e730857893742f73a082f6b8d139casatok * Cancel pending and running spell check tasks 158b4aff97c85e730857893742f73a082f6b8d139casatok */ 159b4aff97c85e730857893742f73a082f6b8d139casatok public void cancel() { 160b4aff97c85e730857893742f73a082f6b8d139casatok mSpellCheckerSessionListenerImpl.cancel(); 161b4aff97c85e730857893742f73a082f6b8d139casatok } 162b4aff97c85e730857893742f73a082f6b8d139casatok 163b4aff97c85e730857893742f73a082f6b8d139casatok /** 164988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Finish this session and allow TextServicesManagerService to disconnect the bound spell 165988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * checker. 166988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 167988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void close() { 168988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mIsUsed = false; 169988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 17074061ff90453c79ddbde468f630a41425da07710satok mSpellCheckerSessionListenerImpl.close(); 171988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl); 172988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 173988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // do nothing 174988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 175988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 176988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 177988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 1780dc1f648a09b46c45190ba1ce7daecf7fada4347satok * @hide 1790dc1f648a09b46c45190ba1ce7daecf7fada4347satok */ 1800dc1f648a09b46c45190ba1ce7daecf7fada4347satok public void getSuggestionsForSentence(TextInfo textInfo, int suggestionsLimit) { 1810dc1f648a09b46c45190ba1ce7daecf7fada4347satok mSpellCheckerSessionListenerImpl.getSuggestionsMultipleForSentence( 1820dc1f648a09b46c45190ba1ce7daecf7fada4347satok new TextInfo[] {textInfo}, suggestionsLimit); 1830dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 1840dc1f648a09b46c45190ba1ce7daecf7fada4347satok 1850dc1f648a09b46c45190ba1ce7daecf7fada4347satok /** 186988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Get candidate strings for a substring of the specified text. 187988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param textInfo text metadata for a spell checker 188988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param suggestionsLimit the number of limit of suggestions returned 189988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 190988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void getSuggestions(TextInfo textInfo, int suggestionsLimit) { 191988323c57bd25a58f05dfa492d9b9c8ab62c5153satok getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false); 192988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 193988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 194988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 195988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * A batch process of getSuggestions 196988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param textInfos an array of text metadata for a spell checker 197988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param suggestionsLimit the number of limit of suggestions returned 198988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param sequentialWords true if textInfos can be treated as sequential words. 199988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 200988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void getSuggestions( 201988323c57bd25a58f05dfa492d9b9c8ab62c5153satok TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 2026be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 2036be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); 2046be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 205988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: Handle multiple words suggestions by using WordBreakIterator 206988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( 207988323c57bd25a58f05dfa492d9b9c8ab62c5153satok textInfos, suggestionsLimit, sequentialWords); 208988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 209988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 210988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) { 211988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos); 212988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 213988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 2140dc1f648a09b46c45190ba1ce7daecf7fada4347satok private void handleOnGetSuggestionsMultipleForSentence(SuggestionsInfo[] suggestionInfos) { 2150dc1f648a09b46c45190ba1ce7daecf7fada4347satok mSpellCheckerSessionListener.onGetSuggestionsForSentence(suggestionInfos); 2160dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 2170dc1f648a09b46c45190ba1ce7daecf7fada4347satok 218988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub { 219988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final int TASK_CANCEL = 1; 220988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2; 22174061ff90453c79ddbde468f630a41425da07710satok private static final int TASK_CLOSE = 3; 2220dc1f648a09b46c45190ba1ce7daecf7fada4347satok private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4; 223988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final Queue<SpellCheckerParams> mPendingTasks = 224988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new LinkedList<SpellCheckerParams>(); 225060677f4686a93d92117d7d472e754423a368bdbsatok private Handler mHandler; 226988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 227988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private boolean mOpened; 228988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private ISpellCheckerSession mISpellCheckerSession; 229988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 230988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerSessionListenerImpl(Handler handler) { 231988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mOpened = false; 232988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mHandler = handler; 233988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 234988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 235988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static class SpellCheckerParams { 236988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final int mWhat; 237988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final TextInfo[] mTextInfos; 238988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final int mSuggestionsLimit; 239988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final boolean mSequentialWords; 240988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit, 241988323c57bd25a58f05dfa492d9b9c8ab62c5153satok boolean sequentialWords) { 242988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mWhat = what; 243988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTextInfos = textInfos; 244988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSuggestionsLimit = suggestionsLimit; 245988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSequentialWords = sequentialWords; 246988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 247988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 248988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 249988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processTask(SpellCheckerParams scp) { 250988323c57bd25a58f05dfa492d9b9c8ab62c5153satok switch (scp.mWhat) { 251988323c57bd25a58f05dfa492d9b9c8ab62c5153satok case TASK_CANCEL: 252988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processCancel(); 253988323c57bd25a58f05dfa492d9b9c8ab62c5153satok break; 254988323c57bd25a58f05dfa492d9b9c8ab62c5153satok case TASK_GET_SUGGESTIONS_MULTIPLE: 255988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processGetSuggestionsMultiple(scp); 256988323c57bd25a58f05dfa492d9b9c8ab62c5153satok break; 25774061ff90453c79ddbde468f630a41425da07710satok case TASK_CLOSE: 25874061ff90453c79ddbde468f630a41425da07710satok processClose(); 25974061ff90453c79ddbde468f630a41425da07710satok break; 2600dc1f648a09b46c45190ba1ce7daecf7fada4347satok case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: 2610dc1f648a09b46c45190ba1ce7daecf7fada4347satok processGetSuggestionsMultipleForSentence(scp); 2620dc1f648a09b46c45190ba1ce7daecf7fada4347satok break; 263988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 264988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 265988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 266988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public synchronized void onServiceConnected(ISpellCheckerSession session) { 267988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mISpellCheckerSession = session; 268988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mOpened = true; 269988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (DBG) 270988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.d(TAG, "onServiceConnected - Success"); 271988323c57bd25a58f05dfa492d9b9c8ab62c5153satok while (!mPendingTasks.isEmpty()) { 272988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processTask(mPendingTasks.poll()); 273988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 274988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 275988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 276b4aff97c85e730857893742f73a082f6b8d139casatok public void cancel() { 277b4aff97c85e730857893742f73a082f6b8d139casatok if (DBG) { 278b4aff97c85e730857893742f73a082f6b8d139casatok Log.w(TAG, "cancel"); 279b4aff97c85e730857893742f73a082f6b8d139casatok } 280b4aff97c85e730857893742f73a082f6b8d139casatok processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false)); 281b4aff97c85e730857893742f73a082f6b8d139casatok } 282b4aff97c85e730857893742f73a082f6b8d139casatok 283988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void getSuggestionsMultiple( 284988323c57bd25a58f05dfa492d9b9c8ab62c5153satok TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 2856be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 2866be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "getSuggestionsMultiple"); 2876be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 288988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processOrEnqueueTask( 289988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos, 290988323c57bd25a58f05dfa492d9b9c8ab62c5153satok suggestionsLimit, sequentialWords)); 291988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 292988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 2930dc1f648a09b46c45190ba1ce7daecf7fada4347satok public void getSuggestionsMultipleForSentence(TextInfo[] textInfos, int suggestionsLimit) { 2940dc1f648a09b46c45190ba1ce7daecf7fada4347satok if (DBG) { 2950dc1f648a09b46c45190ba1ce7daecf7fada4347satok Log.w(TAG, "getSuggestionsMultipleForSentence"); 2960dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 2970dc1f648a09b46c45190ba1ce7daecf7fada4347satok processOrEnqueueTask( 2980dc1f648a09b46c45190ba1ce7daecf7fada4347satok new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE, 2990dc1f648a09b46c45190ba1ce7daecf7fada4347satok textInfos, suggestionsLimit, false)); 3000dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 3010dc1f648a09b46c45190ba1ce7daecf7fada4347satok 30274061ff90453c79ddbde468f630a41425da07710satok public void close() { 30374061ff90453c79ddbde468f630a41425da07710satok if (DBG) { 30474061ff90453c79ddbde468f630a41425da07710satok Log.w(TAG, "close"); 30574061ff90453c79ddbde468f630a41425da07710satok } 30674061ff90453c79ddbde468f630a41425da07710satok processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false)); 30774061ff90453c79ddbde468f630a41425da07710satok } 30874061ff90453c79ddbde468f630a41425da07710satok 309988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean isDisconnected() { 310988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mOpened && mISpellCheckerSession == null; 311988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 312988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 313988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean checkOpenConnection() { 314988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mISpellCheckerSession != null) { 315988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return true; 316988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 317988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "not connected to the spellchecker service."); 318988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return false; 319988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 320988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 321988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processOrEnqueueTask(SpellCheckerParams scp) { 3226be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 3236be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession); 3246be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 325b4aff97c85e730857893742f73a082f6b8d139casatok SpellCheckerParams closeTask = null; 326988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mISpellCheckerSession == null) { 327b4aff97c85e730857893742f73a082f6b8d139casatok if (scp.mWhat == TASK_CANCEL) { 328b4aff97c85e730857893742f73a082f6b8d139casatok while (!mPendingTasks.isEmpty()) { 329b4aff97c85e730857893742f73a082f6b8d139casatok final SpellCheckerParams tmp = mPendingTasks.poll(); 330b4aff97c85e730857893742f73a082f6b8d139casatok if (tmp.mWhat == TASK_CLOSE) { 331b4aff97c85e730857893742f73a082f6b8d139casatok // Only one close task should be processed, while we need to remove all 332b4aff97c85e730857893742f73a082f6b8d139casatok // close tasks from the queue 333b4aff97c85e730857893742f73a082f6b8d139casatok closeTask = tmp; 334b4aff97c85e730857893742f73a082f6b8d139casatok } 335b4aff97c85e730857893742f73a082f6b8d139casatok } 336b4aff97c85e730857893742f73a082f6b8d139casatok } 337988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mPendingTasks.offer(scp); 338b4aff97c85e730857893742f73a082f6b8d139casatok if (closeTask != null) { 339b4aff97c85e730857893742f73a082f6b8d139casatok mPendingTasks.offer(closeTask); 340b4aff97c85e730857893742f73a082f6b8d139casatok } 341988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } else { 342988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processTask(scp); 343988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 344988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 345988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 346988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processCancel() { 347988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!checkOpenConnection()) { 348988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 349988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 3506be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 3516be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "Cancel spell checker tasks."); 3526be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 353988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 3545357806980269d846a15c845a6fcc0384fb18860satok mISpellCheckerSession.onCancel(); 355988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 356988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "Failed to cancel " + e); 357988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 358988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 359988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 36074061ff90453c79ddbde468f630a41425da07710satok private void processClose() { 36174061ff90453c79ddbde468f630a41425da07710satok if (!checkOpenConnection()) { 36274061ff90453c79ddbde468f630a41425da07710satok return; 36374061ff90453c79ddbde468f630a41425da07710satok } 36474061ff90453c79ddbde468f630a41425da07710satok if (DBG) { 36574061ff90453c79ddbde468f630a41425da07710satok Log.w(TAG, "Close spell checker tasks."); 36674061ff90453c79ddbde468f630a41425da07710satok } 36774061ff90453c79ddbde468f630a41425da07710satok try { 36874061ff90453c79ddbde468f630a41425da07710satok mISpellCheckerSession.onClose(); 36974061ff90453c79ddbde468f630a41425da07710satok mISpellCheckerSession = null; 370060677f4686a93d92117d7d472e754423a368bdbsatok mHandler = null; 37174061ff90453c79ddbde468f630a41425da07710satok } catch (RemoteException e) { 37274061ff90453c79ddbde468f630a41425da07710satok Log.e(TAG, "Failed to close " + e); 37374061ff90453c79ddbde468f630a41425da07710satok } 37474061ff90453c79ddbde468f630a41425da07710satok } 37574061ff90453c79ddbde468f630a41425da07710satok 376988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processGetSuggestionsMultiple(SpellCheckerParams scp) { 377988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!checkOpenConnection()) { 378988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 379988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 3806be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 3816be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "Get suggestions from the spell checker."); 3826be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 383988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 3845357806980269d846a15c845a6fcc0384fb18860satok mISpellCheckerSession.onGetSuggestionsMultiple( 385988323c57bd25a58f05dfa492d9b9c8ab62c5153satok scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords); 386988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 387988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "Failed to get suggestions " + e); 388988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 389988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 390988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 3910dc1f648a09b46c45190ba1ce7daecf7fada4347satok private void processGetSuggestionsMultipleForSentence(SpellCheckerParams scp) { 3920dc1f648a09b46c45190ba1ce7daecf7fada4347satok if (!checkOpenConnection()) { 3930dc1f648a09b46c45190ba1ce7daecf7fada4347satok return; 3940dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 3950dc1f648a09b46c45190ba1ce7daecf7fada4347satok if (DBG) { 3960dc1f648a09b46c45190ba1ce7daecf7fada4347satok Log.w(TAG, "Get suggestions from the spell checker."); 3970dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 3980dc1f648a09b46c45190ba1ce7daecf7fada4347satok if (scp.mTextInfos.length != 1) { 3990dc1f648a09b46c45190ba1ce7daecf7fada4347satok throw new IllegalArgumentException(); 4000dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 4010dc1f648a09b46c45190ba1ce7daecf7fada4347satok try { 4020dc1f648a09b46c45190ba1ce7daecf7fada4347satok mISpellCheckerSession.onGetSuggestionsMultipleForSentence( 4030dc1f648a09b46c45190ba1ce7daecf7fada4347satok scp.mTextInfos, scp.mSuggestionsLimit); 4040dc1f648a09b46c45190ba1ce7daecf7fada4347satok } catch (RemoteException e) { 4050dc1f648a09b46c45190ba1ce7daecf7fada4347satok Log.e(TAG, "Failed to get suggestions " + e); 4060dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 4070dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 4080dc1f648a09b46c45190ba1ce7daecf7fada4347satok 409988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 410988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onGetSuggestions(SuggestionsInfo[] results) { 411988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results)); 412988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 4130dc1f648a09b46c45190ba1ce7daecf7fada4347satok 4140dc1f648a09b46c45190ba1ce7daecf7fada4347satok @Override 4150dc1f648a09b46c45190ba1ce7daecf7fada4347satok public void onGetSuggestionsForSentence(SuggestionsInfo[] results) { 4160dc1f648a09b46c45190ba1ce7daecf7fada4347satok mHandler.sendMessage( 4170dc1f648a09b46c45190ba1ce7daecf7fada4347satok Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results)); 4180dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 419988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 420988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 421988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 422988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Callback for getting results from text services 423988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 424988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public interface SpellCheckerSessionListener { 425988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 426988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Callback for "getSuggestions" 427988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param results an array of results of getSuggestions 428988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 429988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onGetSuggestions(SuggestionsInfo[] results); 4300dc1f648a09b46c45190ba1ce7daecf7fada4347satok /** 4310dc1f648a09b46c45190ba1ce7daecf7fada4347satok * @hide 4320dc1f648a09b46c45190ba1ce7daecf7fada4347satok */ 4330dc1f648a09b46c45190ba1ce7daecf7fada4347satok public void onGetSuggestionsForSentence(SuggestionsInfo[] results); 434988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 435988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 436a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard private static class InternalListener extends ITextServicesSessionListener.Stub { 437a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard private final SpellCheckerSessionListenerImpl mParentSpellCheckerSessionListenerImpl; 438a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard 439a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard public InternalListener(SpellCheckerSessionListenerImpl spellCheckerSessionListenerImpl) { 440a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard mParentSpellCheckerSessionListenerImpl = spellCheckerSessionListenerImpl; 441a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard } 442a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard 443988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 444988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceConnected(ISpellCheckerSession session) { 4456be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 4466be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "SpellCheckerSession connected."); 4476be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 448a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard mParentSpellCheckerSessionListenerImpl.onServiceConnected(session); 449988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 450988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 451988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 452988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 453988323c57bd25a58f05dfa492d9b9c8ab62c5153satok protected void finalize() throws Throwable { 454988323c57bd25a58f05dfa492d9b9c8ab62c5153satok super.finalize(); 455988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mIsUsed) { 456988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "SpellCheckerSession was not finished properly." + 457988323c57bd25a58f05dfa492d9b9c8ab62c5153satok "You should call finishShession() when you finished to use a spell checker."); 458988323c57bd25a58f05dfa492d9b9c8ab62c5153satok close(); 459988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 460988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 461988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 462988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 463988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @hide 464988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 465988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public ITextServicesSessionListener getTextServicesSessionListener() { 466988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mInternalListener; 467988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 468988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 469988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 470988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @hide 471988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 472988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public ISpellCheckerSessionListener getSpellCheckerSessionListener() { 473988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerSessionListenerImpl; 474988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 4750dc1f648a09b46c45190ba1ce7daecf7fada4347satok 4760dc1f648a09b46c45190ba1ce7daecf7fada4347satok /** 4770dc1f648a09b46c45190ba1ce7daecf7fada4347satok * @hide 4780dc1f648a09b46c45190ba1ce7daecf7fada4347satok */ 4790dc1f648a09b46c45190ba1ce7daecf7fada4347satok public boolean isSentenceSpellCheckSupported() { 4800dc1f648a09b46c45190ba1ce7daecf7fada4347satok return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK); 4810dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 482988323c57bd25a58f05dfa492d9b9c8ab62c5153satok} 483