SpellCheckerSession.java revision b4aff97c85e730857893742f73a082f6b8d139ca
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"; 91aafd955fa8f5c31c511763c0f826b6d7acf15b9csatok 92988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 93988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1; 94988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 95988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final InternalListener mInternalListener; 96988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final ITextServicesManager mTextServicesManager; 971bedd99761e3d2acdac947d641e7fee5db556141satok private final SpellCheckerInfo mSpellCheckerInfo; 98988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl; 99988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 100988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private boolean mIsUsed; 101988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private SpellCheckerSessionListener mSpellCheckerSessionListener; 102988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 103988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** Handler that will execute the main tasks */ 104988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final Handler mHandler = new Handler() { 105988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 106988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void handleMessage(Message msg) { 107988323c57bd25a58f05dfa492d9b9c8ab62c5153satok switch (msg.what) { 108988323c57bd25a58f05dfa492d9b9c8ab62c5153satok case MSG_ON_GET_SUGGESTION_MULTIPLE: 109988323c57bd25a58f05dfa492d9b9c8ab62c5153satok handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj); 110988323c57bd25a58f05dfa492d9b9c8ab62c5153satok break; 111988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 112988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 113988323c57bd25a58f05dfa492d9b9c8ab62c5153satok }; 114988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 115988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 116988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Constructor 117988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @hide 118988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 1191bedd99761e3d2acdac947d641e7fee5db556141satok public SpellCheckerSession( 1201bedd99761e3d2acdac947d641e7fee5db556141satok SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) { 1211bedd99761e3d2acdac947d641e7fee5db556141satok if (info == null || listener == null || tsm == null) { 122988323c57bd25a58f05dfa492d9b9c8ab62c5153satok throw new NullPointerException(); 123988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 1241bedd99761e3d2acdac947d641e7fee5db556141satok mSpellCheckerInfo = info; 125988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler); 126a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard mInternalListener = new InternalListener(mSpellCheckerSessionListenerImpl); 127988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTextServicesManager = tsm; 128988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mIsUsed = true; 129988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListener = listener; 130988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 131988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 132988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 133988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @return true if the connection to a text service of this session is disconnected and not 134988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * alive. 135988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 136988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean isSessionDisconnected() { 137988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerSessionListenerImpl.isDisconnected(); 138988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 139988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 140988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 1411bedd99761e3d2acdac947d641e7fee5db556141satok * Get the spell checker service info this spell checker session has. 1421bedd99761e3d2acdac947d641e7fee5db556141satok * @return SpellCheckerInfo for the specified locale. 1431bedd99761e3d2acdac947d641e7fee5db556141satok */ 1441bedd99761e3d2acdac947d641e7fee5db556141satok public SpellCheckerInfo getSpellChecker() { 1451bedd99761e3d2acdac947d641e7fee5db556141satok return mSpellCheckerInfo; 1461bedd99761e3d2acdac947d641e7fee5db556141satok } 1471bedd99761e3d2acdac947d641e7fee5db556141satok 1481bedd99761e3d2acdac947d641e7fee5db556141satok /** 149b4aff97c85e730857893742f73a082f6b8d139casatok * Cancel pending and running spell check tasks 150b4aff97c85e730857893742f73a082f6b8d139casatok */ 151b4aff97c85e730857893742f73a082f6b8d139casatok public void cancel() { 152b4aff97c85e730857893742f73a082f6b8d139casatok mSpellCheckerSessionListenerImpl.cancel(); 153b4aff97c85e730857893742f73a082f6b8d139casatok } 154b4aff97c85e730857893742f73a082f6b8d139casatok 155b4aff97c85e730857893742f73a082f6b8d139casatok /** 156988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Finish this session and allow TextServicesManagerService to disconnect the bound spell 157988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * checker. 158988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 159988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void close() { 160988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mIsUsed = false; 161988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 16274061ff90453c79ddbde468f630a41425da07710satok mSpellCheckerSessionListenerImpl.close(); 163988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl); 164988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 165988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // do nothing 166988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 167988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 168988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 169988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 170988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Get candidate strings for a substring of the specified text. 171988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param textInfo text metadata for a spell checker 172988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param suggestionsLimit the number of limit of suggestions returned 173988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 174988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void getSuggestions(TextInfo textInfo, int suggestionsLimit) { 175988323c57bd25a58f05dfa492d9b9c8ab62c5153satok getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false); 176988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 177988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 178988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 179988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * A batch process of getSuggestions 180988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param textInfos an array of text metadata for a spell checker 181988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param suggestionsLimit the number of limit of suggestions returned 182988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param sequentialWords true if textInfos can be treated as sequential words. 183988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 184988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void getSuggestions( 185988323c57bd25a58f05dfa492d9b9c8ab62c5153satok TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 1866be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 1876be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); 1886be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 189988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: Handle multiple words suggestions by using WordBreakIterator 190988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( 191988323c57bd25a58f05dfa492d9b9c8ab62c5153satok textInfos, suggestionsLimit, sequentialWords); 192988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 193988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 194988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) { 195988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos); 196988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 197988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 198988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub { 199988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final int TASK_CANCEL = 1; 200988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2; 20174061ff90453c79ddbde468f630a41425da07710satok private static final int TASK_CLOSE = 3; 202988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final Queue<SpellCheckerParams> mPendingTasks = 203988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new LinkedList<SpellCheckerParams>(); 204988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final Handler mHandler; 205988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 206988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private boolean mOpened; 207988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private ISpellCheckerSession mISpellCheckerSession; 208988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 209988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerSessionListenerImpl(Handler handler) { 210988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mOpened = false; 211988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mHandler = handler; 212988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 213988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 214988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static class SpellCheckerParams { 215988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final int mWhat; 216988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final TextInfo[] mTextInfos; 217988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final int mSuggestionsLimit; 218988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final boolean mSequentialWords; 219988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit, 220988323c57bd25a58f05dfa492d9b9c8ab62c5153satok boolean sequentialWords) { 221988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mWhat = what; 222988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTextInfos = textInfos; 223988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSuggestionsLimit = suggestionsLimit; 224988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSequentialWords = sequentialWords; 225988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 226988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 227988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 228988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processTask(SpellCheckerParams scp) { 229988323c57bd25a58f05dfa492d9b9c8ab62c5153satok switch (scp.mWhat) { 230988323c57bd25a58f05dfa492d9b9c8ab62c5153satok case TASK_CANCEL: 231988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processCancel(); 232988323c57bd25a58f05dfa492d9b9c8ab62c5153satok break; 233988323c57bd25a58f05dfa492d9b9c8ab62c5153satok case TASK_GET_SUGGESTIONS_MULTIPLE: 234988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processGetSuggestionsMultiple(scp); 235988323c57bd25a58f05dfa492d9b9c8ab62c5153satok break; 23674061ff90453c79ddbde468f630a41425da07710satok case TASK_CLOSE: 23774061ff90453c79ddbde468f630a41425da07710satok processClose(); 23874061ff90453c79ddbde468f630a41425da07710satok break; 239988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 240988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 241988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 242988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public synchronized void onServiceConnected(ISpellCheckerSession session) { 243988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mISpellCheckerSession = session; 244988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mOpened = true; 245988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (DBG) 246988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.d(TAG, "onServiceConnected - Success"); 247988323c57bd25a58f05dfa492d9b9c8ab62c5153satok while (!mPendingTasks.isEmpty()) { 248988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processTask(mPendingTasks.poll()); 249988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 250988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 251988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 252b4aff97c85e730857893742f73a082f6b8d139casatok public void cancel() { 253b4aff97c85e730857893742f73a082f6b8d139casatok if (DBG) { 254b4aff97c85e730857893742f73a082f6b8d139casatok Log.w(TAG, "cancel"); 255b4aff97c85e730857893742f73a082f6b8d139casatok } 256b4aff97c85e730857893742f73a082f6b8d139casatok processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false)); 257b4aff97c85e730857893742f73a082f6b8d139casatok } 258b4aff97c85e730857893742f73a082f6b8d139casatok 259988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void getSuggestionsMultiple( 260988323c57bd25a58f05dfa492d9b9c8ab62c5153satok TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 2616be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 2626be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "getSuggestionsMultiple"); 2636be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 264988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processOrEnqueueTask( 265988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos, 266988323c57bd25a58f05dfa492d9b9c8ab62c5153satok suggestionsLimit, sequentialWords)); 267988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 268988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 26974061ff90453c79ddbde468f630a41425da07710satok public void close() { 27074061ff90453c79ddbde468f630a41425da07710satok if (DBG) { 27174061ff90453c79ddbde468f630a41425da07710satok Log.w(TAG, "close"); 27274061ff90453c79ddbde468f630a41425da07710satok } 27374061ff90453c79ddbde468f630a41425da07710satok processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false)); 27474061ff90453c79ddbde468f630a41425da07710satok } 27574061ff90453c79ddbde468f630a41425da07710satok 276988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean isDisconnected() { 277988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mOpened && mISpellCheckerSession == null; 278988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 279988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 280988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean checkOpenConnection() { 281988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mISpellCheckerSession != null) { 282988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return true; 283988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 284988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "not connected to the spellchecker service."); 285988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return false; 286988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 287988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 288988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processOrEnqueueTask(SpellCheckerParams scp) { 2896be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 2906be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession); 2916be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 292b4aff97c85e730857893742f73a082f6b8d139casatok SpellCheckerParams closeTask = null; 293988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mISpellCheckerSession == null) { 294b4aff97c85e730857893742f73a082f6b8d139casatok if (scp.mWhat == TASK_CANCEL) { 295b4aff97c85e730857893742f73a082f6b8d139casatok while (!mPendingTasks.isEmpty()) { 296b4aff97c85e730857893742f73a082f6b8d139casatok final SpellCheckerParams tmp = mPendingTasks.poll(); 297b4aff97c85e730857893742f73a082f6b8d139casatok if (tmp.mWhat == TASK_CLOSE) { 298b4aff97c85e730857893742f73a082f6b8d139casatok // Only one close task should be processed, while we need to remove all 299b4aff97c85e730857893742f73a082f6b8d139casatok // close tasks from the queue 300b4aff97c85e730857893742f73a082f6b8d139casatok closeTask = tmp; 301b4aff97c85e730857893742f73a082f6b8d139casatok } 302b4aff97c85e730857893742f73a082f6b8d139casatok } 303b4aff97c85e730857893742f73a082f6b8d139casatok } 304988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mPendingTasks.offer(scp); 305b4aff97c85e730857893742f73a082f6b8d139casatok if (closeTask != null) { 306b4aff97c85e730857893742f73a082f6b8d139casatok mPendingTasks.offer(closeTask); 307b4aff97c85e730857893742f73a082f6b8d139casatok } 308988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } else { 309988323c57bd25a58f05dfa492d9b9c8ab62c5153satok processTask(scp); 310988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 311988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 312988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 313988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processCancel() { 314988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!checkOpenConnection()) { 315988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 316988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 3176be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 3186be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "Cancel spell checker tasks."); 3196be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 320988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 3215357806980269d846a15c845a6fcc0384fb18860satok mISpellCheckerSession.onCancel(); 322988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 323988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "Failed to cancel " + e); 324988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 325988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 326988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 32774061ff90453c79ddbde468f630a41425da07710satok private void processClose() { 32874061ff90453c79ddbde468f630a41425da07710satok if (!checkOpenConnection()) { 32974061ff90453c79ddbde468f630a41425da07710satok return; 33074061ff90453c79ddbde468f630a41425da07710satok } 33174061ff90453c79ddbde468f630a41425da07710satok if (DBG) { 33274061ff90453c79ddbde468f630a41425da07710satok Log.w(TAG, "Close spell checker tasks."); 33374061ff90453c79ddbde468f630a41425da07710satok } 33474061ff90453c79ddbde468f630a41425da07710satok try { 33574061ff90453c79ddbde468f630a41425da07710satok mISpellCheckerSession.onClose(); 33674061ff90453c79ddbde468f630a41425da07710satok mISpellCheckerSession = null; 33774061ff90453c79ddbde468f630a41425da07710satok } catch (RemoteException e) { 33874061ff90453c79ddbde468f630a41425da07710satok Log.e(TAG, "Failed to close " + e); 33974061ff90453c79ddbde468f630a41425da07710satok } 34074061ff90453c79ddbde468f630a41425da07710satok } 34174061ff90453c79ddbde468f630a41425da07710satok 342988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void processGetSuggestionsMultiple(SpellCheckerParams scp) { 343988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!checkOpenConnection()) { 344988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 345988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 3466be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 3476be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "Get suggestions from the spell checker."); 3486be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 349988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 3505357806980269d846a15c845a6fcc0384fb18860satok mISpellCheckerSession.onGetSuggestionsMultiple( 351988323c57bd25a58f05dfa492d9b9c8ab62c5153satok scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords); 352988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 353988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "Failed to get suggestions " + e); 354988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 355988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 356988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 357988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 358988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onGetSuggestions(SuggestionsInfo[] results) { 359988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results)); 360988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 361988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 362988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 363988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 364988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Callback for getting results from text services 365988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 366988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public interface SpellCheckerSessionListener { 367988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 368988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Callback for "getSuggestions" 369988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @param results an array of results of getSuggestions 370988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 371988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onGetSuggestions(SuggestionsInfo[] results); 372988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 373988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 374a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard private static class InternalListener extends ITextServicesSessionListener.Stub { 375a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard private final SpellCheckerSessionListenerImpl mParentSpellCheckerSessionListenerImpl; 376a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard 377a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard public InternalListener(SpellCheckerSessionListenerImpl spellCheckerSessionListenerImpl) { 378a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard mParentSpellCheckerSessionListenerImpl = spellCheckerSessionListenerImpl; 379a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard } 380a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard 381988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 382988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceConnected(ISpellCheckerSession session) { 3836be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 3846be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Log.w(TAG, "SpellCheckerSession connected."); 3856be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 386a80838d9d63fcc9a83a9e7c99884e5b50316d4f0Jean Chalard mParentSpellCheckerSessionListenerImpl.onServiceConnected(session); 387988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 388988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 389988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 390988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 391988323c57bd25a58f05dfa492d9b9c8ab62c5153satok protected void finalize() throws Throwable { 392988323c57bd25a58f05dfa492d9b9c8ab62c5153satok super.finalize(); 393988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mIsUsed) { 394988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Log.e(TAG, "SpellCheckerSession was not finished properly." + 395988323c57bd25a58f05dfa492d9b9c8ab62c5153satok "You should call finishShession() when you finished to use a spell checker."); 396988323c57bd25a58f05dfa492d9b9c8ab62c5153satok close(); 397988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 398988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 399988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 400988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 401988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @hide 402988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 403988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public ITextServicesSessionListener getTextServicesSessionListener() { 404988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mInternalListener; 405988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 406988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 407988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 408988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * @hide 409988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 410988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public ISpellCheckerSessionListener getSpellCheckerSessionListener() { 411988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerSessionListenerImpl; 412988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 413988323c57bd25a58f05dfa492d9b9c8ab62c5153satok} 414