1988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/* 2988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Copyright (C) 2011 The Android Open Source Project 3988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * 4988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * use this file except in compliance with the License. You may obtain a copy of 6988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * 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, WITHOUT 12988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * License for the specific language governing permissions and limitations under 14988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * the License. 15988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 16988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 17988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpackage android.service.textservice; 18988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 19988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerService; 20988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSession; 21988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSessionListener; 22988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 23988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.app.Service; 24988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Intent; 255357806980269d846a15c845a6fcc0384fb18860satokimport android.os.Bundle; 26988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.IBinder; 2733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackbornimport android.os.Process; 28988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.RemoteException; 29c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satokimport android.text.TextUtils; 30c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satokimport android.text.method.WordIterator; 316be6d7548fb7c29a4d46dc985318ab2adf69f95fsatokimport android.util.Log; 32d404fe110558bd2e1960b428db6a2ee8bfd040cdsatokimport android.view.textservice.SentenceSuggestionsInfo; 33988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.SuggestionsInfo; 34988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.TextInfo; 35988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 36988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.lang.ref.WeakReference; 37c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satokimport java.text.BreakIterator; 38c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satokimport java.util.ArrayList; 39c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satokimport java.util.Locale; 40988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 41988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/** 42988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * SpellCheckerService provides an abstract base class for a spell checker. 43988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * This class combines a service to the system with the spell checker service interface that 44988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * spell checker must implement. 4544b75030931d9c65c9e495a86d11d71da59b4429satok * 4644b75030931d9c65c9e495a86d11d71da59b4429satok * <p>In addition to the normal Service lifecycle methods, this class 4744b75030931d9c65c9e495a86d11d71da59b4429satok * introduces a new specific callback that subclasses should override 4844b75030931d9c65c9e495a86d11d71da59b4429satok * {@link #createSession()} to provide a spell checker session that is corresponding 4944b75030931d9c65c9e495a86d11d71da59b4429satok * to requested language and so on. The spell checker session returned by this method 5044b75030931d9c65c9e495a86d11d71da59b4429satok * should extend {@link SpellCheckerService.Session}. 5144b75030931d9c65c9e495a86d11d71da59b4429satok * </p> 5244b75030931d9c65c9e495a86d11d71da59b4429satok * 5344b75030931d9c65c9e495a86d11d71da59b4429satok * <h3>Returning spell check results</h3> 5444b75030931d9c65c9e495a86d11d71da59b4429satok * 5544b75030931d9c65c9e495a86d11d71da59b4429satok * <p>{@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 5644b75030931d9c65c9e495a86d11d71da59b4429satok * should return spell check results. 5744b75030931d9c65c9e495a86d11d71da59b4429satok * It receives {@link android.view.textservice.TextInfo} and returns 5844b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.view.textservice.SuggestionsInfo} for the input. 5944b75030931d9c65c9e495a86d11d71da59b4429satok * You may want to override 6044b75030931d9c65c9e495a86d11d71da59b4429satok * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} for 6144b75030931d9c65c9e495a86d11d71da59b4429satok * better performance and quality. 6244b75030931d9c65c9e495a86d11d71da59b4429satok * </p> 6344b75030931d9c65c9e495a86d11d71da59b4429satok * 6444b75030931d9c65c9e495a86d11d71da59b4429satok * <p>Please note that {@link SpellCheckerService.Session#getLocale()} does not return a valid 6544b75030931d9c65c9e495a86d11d71da59b4429satok * locale before {@link SpellCheckerService.Session#onCreate()} </p> 6644b75030931d9c65c9e495a86d11d71da59b4429satok * 67988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 68988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpublic abstract class SpellCheckerService extends Service { 69988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final String TAG = SpellCheckerService.class.getSimpleName(); 706be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok private static final boolean DBG = false; 71142d7575b52d03d46246e3b142e22ebc32d45a84satok public static final String SERVICE_INTERFACE = 72142d7575b52d03d46246e3b142e22ebc32d45a84satok "android.service.textservice.SpellCheckerService"; 73988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 74988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this); 75988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 76988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 77988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 785357806980269d846a15c845a6fcc0384fb18860satok * Implement to return the implementation of the internal spell checker 795357806980269d846a15c845a6fcc0384fb18860satok * service interface. Subclasses should not override. 80988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 815357806980269d846a15c845a6fcc0384fb18860satok @Override 825357806980269d846a15c845a6fcc0384fb18860satok public final IBinder onBind(final Intent intent) { 835357806980269d846a15c845a6fcc0384fb18860satok if (DBG) { 845357806980269d846a15c845a6fcc0384fb18860satok Log.w(TAG, "onBind"); 85988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 865357806980269d846a15c845a6fcc0384fb18860satok return mBinder; 87988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 88988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 89988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 905357806980269d846a15c845a6fcc0384fb18860satok * Factory method to create a spell checker session impl 915357806980269d846a15c845a6fcc0384fb18860satok * @return SpellCheckerSessionImpl which should be overridden by a concrete implementation. 92988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 935357806980269d846a15c845a6fcc0384fb18860satok public abstract Session createSession(); 94988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 95988323c57bd25a58f05dfa492d9b9c8ab62c5153satok /** 965357806980269d846a15c845a6fcc0384fb18860satok * This abstract class should be overridden by a concrete implementation of a spell checker. 97988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 98117999d1f44ec3423369385495ae207898b7b73esatok public static abstract class Session { 995357806980269d846a15c845a6fcc0384fb18860satok private InternalISpellCheckerSession mInternalSession; 100c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok private volatile SentenceLevelAdapter mSentenceLevelAdapter; 1015357806980269d846a15c845a6fcc0384fb18860satok 1025357806980269d846a15c845a6fcc0384fb18860satok /** 1035357806980269d846a15c845a6fcc0384fb18860satok * @hide 1045357806980269d846a15c845a6fcc0384fb18860satok */ 1055357806980269d846a15c845a6fcc0384fb18860satok public final void setInternalISpellCheckerSession(InternalISpellCheckerSession session) { 1065357806980269d846a15c845a6fcc0384fb18860satok mInternalSession = session; 1075357806980269d846a15c845a6fcc0384fb18860satok } 1085357806980269d846a15c845a6fcc0384fb18860satok 1095357806980269d846a15c845a6fcc0384fb18860satok /** 1105357806980269d846a15c845a6fcc0384fb18860satok * This is called after the class is initialized, at which point it knows it can call 1115357806980269d846a15c845a6fcc0384fb18860satok * getLocale() etc... 1125357806980269d846a15c845a6fcc0384fb18860satok */ 1135357806980269d846a15c845a6fcc0384fb18860satok public abstract void onCreate(); 1145357806980269d846a15c845a6fcc0384fb18860satok 1155357806980269d846a15c845a6fcc0384fb18860satok /** 1165357806980269d846a15c845a6fcc0384fb18860satok * Get suggestions for specified text in TextInfo. 1175357806980269d846a15c845a6fcc0384fb18860satok * This function will run on the incoming IPC thread. 1185357806980269d846a15c845a6fcc0384fb18860satok * So, this is not called on the main thread, 1195357806980269d846a15c845a6fcc0384fb18860satok * but will be called in series on another thread. 1205357806980269d846a15c845a6fcc0384fb18860satok * @param textInfo the text metadata 1216183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * @param suggestionsLimit the maximum number of suggestions to be returned 12244b75030931d9c65c9e495a86d11d71da59b4429satok * @return SuggestionsInfo which contains suggestions for textInfo 1235357806980269d846a15c845a6fcc0384fb18860satok */ 1245357806980269d846a15c845a6fcc0384fb18860satok public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit); 1255357806980269d846a15c845a6fcc0384fb18860satok 1265357806980269d846a15c845a6fcc0384fb18860satok /** 1275357806980269d846a15c845a6fcc0384fb18860satok * A batch process of onGetSuggestions. 1285357806980269d846a15c845a6fcc0384fb18860satok * This function will run on the incoming IPC thread. 1295357806980269d846a15c845a6fcc0384fb18860satok * So, this is not called on the main thread, 1305357806980269d846a15c845a6fcc0384fb18860satok * but will be called in series on another thread. 1315357806980269d846a15c845a6fcc0384fb18860satok * @param textInfos an array of the text metadata 1326183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * @param suggestionsLimit the maximum number of suggestions to be returned 1335357806980269d846a15c845a6fcc0384fb18860satok * @param sequentialWords true if textInfos can be treated as sequential words. 1346183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * @return an array of {@link SentenceSuggestionsInfo} returned by 1356183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 1365357806980269d846a15c845a6fcc0384fb18860satok */ 1375357806980269d846a15c845a6fcc0384fb18860satok public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos, 1385357806980269d846a15c845a6fcc0384fb18860satok int suggestionsLimit, boolean sequentialWords) { 1395357806980269d846a15c845a6fcc0384fb18860satok final int length = textInfos.length; 1405357806980269d846a15c845a6fcc0384fb18860satok final SuggestionsInfo[] retval = new SuggestionsInfo[length]; 1415357806980269d846a15c845a6fcc0384fb18860satok for (int i = 0; i < length; ++i) { 1425357806980269d846a15c845a6fcc0384fb18860satok retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit); 1435357806980269d846a15c845a6fcc0384fb18860satok retval[i].setCookieAndSequence( 1445357806980269d846a15c845a6fcc0384fb18860satok textInfos[i].getCookie(), textInfos[i].getSequence()); 1455357806980269d846a15c845a6fcc0384fb18860satok } 1465357806980269d846a15c845a6fcc0384fb18860satok return retval; 1475357806980269d846a15c845a6fcc0384fb18860satok } 1485357806980269d846a15c845a6fcc0384fb18860satok 1495357806980269d846a15c845a6fcc0384fb18860satok /** 150431ea84e1fde20139b748a4818c44e85a715e155satok * Get sentence suggestions for specified texts in an array of TextInfo. 151c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok * The default implementation splits the input text to words and returns 152c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok * {@link SentenceSuggestionsInfo} which contains suggestions for each word. 153431ea84e1fde20139b748a4818c44e85a715e155satok * This function will run on the incoming IPC thread. 154431ea84e1fde20139b748a4818c44e85a715e155satok * So, this is not called on the main thread, 155431ea84e1fde20139b748a4818c44e85a715e155satok * but will be called in series on another thread. 1560dc1f648a09b46c45190ba1ce7daecf7fada4347satok * When you override this method, make sure that suggestionsLimit is applied to suggestions 1570dc1f648a09b46c45190ba1ce7daecf7fada4347satok * that share the same start position and length. 1586183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * @param textInfos an array of the text metadata 1596183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * @param suggestionsLimit the maximum number of suggestions to be returned 1606183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * @return an array of {@link SentenceSuggestionsInfo} returned by 1616183cd64a98a69ea247813c9ba0a07326c4bc1aesatok * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 1620dc1f648a09b46c45190ba1ce7daecf7fada4347satok */ 163d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, 1640dc1f648a09b46c45190ba1ce7daecf7fada4347satok int suggestionsLimit) { 165c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (textInfos == null || textInfos.length == 0) { 166c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS; 167c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 168c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (DBG) { 169c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok Log.d(TAG, "onGetSentenceSuggestionsMultiple: + " + textInfos.length + ", " 170c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok + suggestionsLimit); 171c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 172c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (mSentenceLevelAdapter == null) { 173c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok synchronized(this) { 174c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (mSentenceLevelAdapter == null) { 175c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final String localeStr = getLocale(); 176c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (!TextUtils.isEmpty(localeStr)) { 177c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mSentenceLevelAdapter = new SentenceLevelAdapter(new Locale(localeStr)); 178c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 179c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 180c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 181c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 182c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (mSentenceLevelAdapter == null) { 183c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS; 184c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 185c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int infosSize = textInfos.length; 186c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize]; 187c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok for (int i = 0; i < infosSize; ++i) { 188c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams = 189c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mSentenceLevelAdapter.getSplitWords(textInfos[i]); 190c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems = 191c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok textInfoParams.mItems; 192c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int itemsSize = mItems.size(); 193c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final TextInfo[] splitTextInfos = new TextInfo[itemsSize]; 194c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok for (int j = 0; j < itemsSize; ++j) { 195c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok splitTextInfos[j] = mItems.get(j).mTextInfo; 196c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 197c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok retval[i] = SentenceLevelAdapter.reconstructSuggestions( 198c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok textInfoParams, onGetSuggestionsMultiple( 199c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok splitTextInfos, suggestionsLimit, true)); 2000dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 2010dc1f648a09b46c45190ba1ce7daecf7fada4347satok return retval; 2020dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 2030dc1f648a09b46c45190ba1ce7daecf7fada4347satok 2040dc1f648a09b46c45190ba1ce7daecf7fada4347satok /** 2055357806980269d846a15c845a6fcc0384fb18860satok * Request to abort all tasks executed in SpellChecker. 2065357806980269d846a15c845a6fcc0384fb18860satok * This function will run on the incoming IPC thread. 2075357806980269d846a15c845a6fcc0384fb18860satok * So, this is not called on the main thread, 2085357806980269d846a15c845a6fcc0384fb18860satok * but will be called in series on another thread. 2095357806980269d846a15c845a6fcc0384fb18860satok */ 2105357806980269d846a15c845a6fcc0384fb18860satok public void onCancel() {} 2115357806980269d846a15c845a6fcc0384fb18860satok 2125357806980269d846a15c845a6fcc0384fb18860satok /** 21374061ff90453c79ddbde468f630a41425da07710satok * Request to close this session. 21474061ff90453c79ddbde468f630a41425da07710satok * This function will run on the incoming IPC thread. 21574061ff90453c79ddbde468f630a41425da07710satok * So, this is not called on the main thread, 21674061ff90453c79ddbde468f630a41425da07710satok * but will be called in series on another thread. 21774061ff90453c79ddbde468f630a41425da07710satok */ 21874061ff90453c79ddbde468f630a41425da07710satok public void onClose() {} 21974061ff90453c79ddbde468f630a41425da07710satok 22074061ff90453c79ddbde468f630a41425da07710satok /** 2215357806980269d846a15c845a6fcc0384fb18860satok * @return Locale for this session 2225357806980269d846a15c845a6fcc0384fb18860satok */ 2235357806980269d846a15c845a6fcc0384fb18860satok public String getLocale() { 2245357806980269d846a15c845a6fcc0384fb18860satok return mInternalSession.getLocale(); 2255357806980269d846a15c845a6fcc0384fb18860satok } 2265357806980269d846a15c845a6fcc0384fb18860satok 2275357806980269d846a15c845a6fcc0384fb18860satok /** 2285357806980269d846a15c845a6fcc0384fb18860satok * @return Bundle for this session 2295357806980269d846a15c845a6fcc0384fb18860satok */ 2305357806980269d846a15c845a6fcc0384fb18860satok public Bundle getBundle() { 2315357806980269d846a15c845a6fcc0384fb18860satok return mInternalSession.getBundle(); 2326be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 233988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 234988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 2355357806980269d846a15c845a6fcc0384fb18860satok // Preventing from exposing ISpellCheckerSession.aidl, create an internal class. 2365357806980269d846a15c845a6fcc0384fb18860satok private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub { 23774061ff90453c79ddbde468f630a41425da07710satok private ISpellCheckerSessionListener mListener; 2385357806980269d846a15c845a6fcc0384fb18860satok private final Session mSession; 2395357806980269d846a15c845a6fcc0384fb18860satok private final String mLocale; 2405357806980269d846a15c845a6fcc0384fb18860satok private final Bundle mBundle; 241988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 2425357806980269d846a15c845a6fcc0384fb18860satok public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener, 2435357806980269d846a15c845a6fcc0384fb18860satok Bundle bundle, Session session) { 244988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mListener = listener; 2455357806980269d846a15c845a6fcc0384fb18860satok mSession = session; 2465357806980269d846a15c845a6fcc0384fb18860satok mLocale = locale; 2475357806980269d846a15c845a6fcc0384fb18860satok mBundle = bundle; 2485357806980269d846a15c845a6fcc0384fb18860satok session.setInternalISpellCheckerSession(this); 249988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 250988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 251988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 2525357806980269d846a15c845a6fcc0384fb18860satok public void onGetSuggestionsMultiple( 253988323c57bd25a58f05dfa492d9b9c8ab62c5153satok TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 25433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn int pri = Process.getThreadPriority(Process.myTid()); 255988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 25633b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 257988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mListener.onGetSuggestions( 2585357806980269d846a15c845a6fcc0384fb18860satok mSession.onGetSuggestionsMultiple( 2595357806980269d846a15c845a6fcc0384fb18860satok textInfos, suggestionsLimit, sequentialWords)); 260988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 26133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn } finally { 26233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn Process.setThreadPriority(pri); 263988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 264988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 265988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 266988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 267d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok public void onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { 2680dc1f648a09b46c45190ba1ce7daecf7fada4347satok try { 269d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok mListener.onGetSentenceSuggestions( 270d404fe110558bd2e1960b428db6a2ee8bfd040cdsatok mSession.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit)); 2710dc1f648a09b46c45190ba1ce7daecf7fada4347satok } catch (RemoteException e) { 2720dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 2730dc1f648a09b46c45190ba1ce7daecf7fada4347satok } 2740dc1f648a09b46c45190ba1ce7daecf7fada4347satok 2750dc1f648a09b46c45190ba1ce7daecf7fada4347satok @Override 2765357806980269d846a15c845a6fcc0384fb18860satok public void onCancel() { 27733b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn int pri = Process.getThreadPriority(Process.myTid()); 27833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn try { 27933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 28033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn mSession.onCancel(); 28133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn } finally { 28233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn Process.setThreadPriority(pri); 28333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn } 2845357806980269d846a15c845a6fcc0384fb18860satok } 2855357806980269d846a15c845a6fcc0384fb18860satok 28674061ff90453c79ddbde468f630a41425da07710satok @Override 28774061ff90453c79ddbde468f630a41425da07710satok public void onClose() { 28833b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn int pri = Process.getThreadPriority(Process.myTid()); 28933b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn try { 29033b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 29133b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn mSession.onClose(); 29233b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn } finally { 29333b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn Process.setThreadPriority(pri); 29433b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn mListener = null; 29533b8ee509f36a0168c8ce5a9091b57ab936f4c13Dianne Hackborn } 29674061ff90453c79ddbde468f630a41425da07710satok } 29774061ff90453c79ddbde468f630a41425da07710satok 2985357806980269d846a15c845a6fcc0384fb18860satok public String getLocale() { 2995357806980269d846a15c845a6fcc0384fb18860satok return mLocale; 3005357806980269d846a15c845a6fcc0384fb18860satok } 3015357806980269d846a15c845a6fcc0384fb18860satok 3025357806980269d846a15c845a6fcc0384fb18860satok public Bundle getBundle() { 3035357806980269d846a15c845a6fcc0384fb18860satok return mBundle; 304988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 305988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 306988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 307988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub { 308988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final WeakReference<SpellCheckerService> mInternalServiceRef; 309988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 310988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerServiceBinder(SpellCheckerService service) { 311988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mInternalServiceRef = new WeakReference<SpellCheckerService>(service); 312988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 313988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 314988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 315988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public ISpellCheckerSession getISpellCheckerSession( 3165357806980269d846a15c845a6fcc0384fb18860satok String locale, ISpellCheckerSessionListener listener, Bundle bundle) { 317988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final SpellCheckerService service = mInternalServiceRef.get(); 318988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (service == null) return null; 3195357806980269d846a15c845a6fcc0384fb18860satok final Session session = service.createSession(); 3205357806980269d846a15c845a6fcc0384fb18860satok final InternalISpellCheckerSession internalSession = 3215357806980269d846a15c845a6fcc0384fb18860satok new InternalISpellCheckerSession(locale, listener, bundle, session); 3225357806980269d846a15c845a6fcc0384fb18860satok session.onCreate(); 3235357806980269d846a15c845a6fcc0384fb18860satok return internalSession; 324988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 325988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 326c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok 327c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok /** 328c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok * Adapter class to accommodate word level spell checking APIs to sentence level spell checking 329c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok * APIs used in 330c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} 331c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok */ 332c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok private static class SentenceLevelAdapter { 333c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS = 334c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok new SentenceSuggestionsInfo[] {}; 335c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null); 336c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok /** 337c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok * Container for split TextInfo parameters 338c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok */ 339c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public static class SentenceWordItem { 340c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public final TextInfo mTextInfo; 341c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public final int mStart; 342c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public final int mLength; 343c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public SentenceWordItem(TextInfo ti, int start, int end) { 344c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mTextInfo = ti; 345c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mStart = start; 346c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mLength = end - start; 347c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 348c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 349c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok 350c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok /** 351c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok * Container for originally queried TextInfo and parameters 352c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok */ 353c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public static class SentenceTextInfoParams { 354c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final TextInfo mOriginalTextInfo; 355c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final ArrayList<SentenceWordItem> mItems; 356c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int mSize; 357c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) { 358c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mOriginalTextInfo = ti; 359c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mItems = items; 360c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mSize = items.size(); 361c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 362c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 363c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok 364c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok private final WordIterator mWordIterator; 365c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public SentenceLevelAdapter(Locale locale) { 366c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok mWordIterator = new WordIterator(locale); 367c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 368c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok 369c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok private SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) { 370c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final WordIterator wordIterator = mWordIterator; 371c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final CharSequence originalText = originalTextInfo.getText(); 372c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int cookie = originalTextInfo.getCookie(); 373c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int start = 0; 374c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int end = originalText.length(); 375c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>(); 376c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok wordIterator.setCharSequence(originalText, 0, originalText.length()); 377c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok int wordEnd = wordIterator.following(start); 378c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok int wordStart = wordIterator.getBeginning(wordEnd); 379c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (DBG) { 380c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok Log.d(TAG, "iterator: break: ---- 1st word start = " + wordStart + ", end = " 381c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok + wordEnd + "\n" + originalText); 382c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 383c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok while (wordStart <= end && wordEnd != BreakIterator.DONE 384c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok && wordStart != BreakIterator.DONE) { 385c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (wordEnd >= start && wordEnd > wordStart) { 386fccd37fb7ef6995c29ced0d2956e91dd84d7586fYohei Yukawa final CharSequence query = originalText.subSequence(wordStart, wordEnd); 387fccd37fb7ef6995c29ced0d2956e91dd84d7586fYohei Yukawa final TextInfo ti = new TextInfo(query, 0, query.length(), cookie, 388fccd37fb7ef6995c29ced0d2956e91dd84d7586fYohei Yukawa query.hashCode()); 389c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd)); 390c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (DBG) { 391c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok Log.d(TAG, "Adapter: word (" + (wordItems.size() - 1) + ") " + query); 392c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 393c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 394c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok wordEnd = wordIterator.following(wordEnd); 395c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (wordEnd == BreakIterator.DONE) { 396c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok break; 397c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 398c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok wordStart = wordIterator.getBeginning(wordEnd); 399c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 400c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok return new SentenceTextInfoParams(originalTextInfo, wordItems); 401c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 402c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok 403c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok public static SentenceSuggestionsInfo reconstructSuggestions( 404c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) { 405c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (results == null || results.length == 0) { 406c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok return null; 407c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 408c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (DBG) { 409c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok Log.w(TAG, "Adapter: onGetSuggestions: got " + results.length); 410c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 411c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (originalTextInfoParams == null) { 412c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (DBG) { 413c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok Log.w(TAG, "Adapter: originalTextInfoParams is null."); 414c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 415c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok return null; 416c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 417c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie(); 418c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int originalSequence = 419c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok originalTextInfoParams.mOriginalTextInfo.getSequence(); 420c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok 421c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int querySize = originalTextInfoParams.mSize; 422c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int[] offsets = new int[querySize]; 423c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int[] lengths = new int[querySize]; 424c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize]; 425c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok for (int i = 0; i < querySize; ++i) { 426c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final SentenceWordItem item = originalTextInfoParams.mItems.get(i); 427c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok SuggestionsInfo result = null; 428c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok for (int j = 0; j < results.length; ++j) { 429c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final SuggestionsInfo cur = results[j]; 430c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) { 431c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok result = cur; 432c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok result.setCookieAndSequence(originalCookie, originalSequence); 433c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok break; 434c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 435c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 436c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok offsets[i] = item.mStart; 437c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok lengths[i] = item.mLength; 438c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO; 439c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok if (DBG) { 440c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok final int size = reconstructedSuggestions[i].getSuggestionsCount(); 441c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok Log.w(TAG, "reconstructedSuggestions(" + i + ")" + size + ", first = " 442c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok + (size > 0 ? reconstructedSuggestions[i].getSuggestionAt(0) 443c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok : "<none>") + ", offset = " + offsets[i] + ", length = " 444c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok + lengths[i]); 445c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 446c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 447c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths); 448c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 449c7ee1b9369ffd7c21a70738056a82dc4238e7fc1satok } 450988323c57bd25a58f05dfa492d9b9c8ab62c5153satok} 451