SpellCheckerService.java revision 6183cd64a98a69ea247813c9ba0a07326c4bc1ae
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package android.service.textservice; 18 19import com.android.internal.textservice.ISpellCheckerService; 20import com.android.internal.textservice.ISpellCheckerSession; 21import com.android.internal.textservice.ISpellCheckerSessionListener; 22 23import android.app.Service; 24import android.content.Intent; 25import android.os.Bundle; 26import android.os.IBinder; 27import android.os.Process; 28import android.os.RemoteException; 29import android.util.Log; 30import android.view.textservice.SentenceSuggestionsInfo; 31import android.view.textservice.SuggestionsInfo; 32import android.view.textservice.TextInfo; 33 34import java.lang.ref.WeakReference; 35 36/** 37 * SpellCheckerService provides an abstract base class for a spell checker. 38 * This class combines a service to the system with the spell checker service interface that 39 * spell checker must implement. 40 * 41 * <p>In addition to the normal Service lifecycle methods, this class 42 * introduces a new specific callback that subclasses should override 43 * {@link #createSession()} to provide a spell checker session that is corresponding 44 * to requested language and so on. The spell checker session returned by this method 45 * should extend {@link SpellCheckerService.Session}. 46 * </p> 47 * 48 * <h3>Returning spell check results</h3> 49 * 50 * <p>{@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 51 * should return spell check results. 52 * It receives {@link android.view.textservice.TextInfo} and returns 53 * {@link android.view.textservice.SuggestionsInfo} for the input. 54 * You may want to override 55 * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} for 56 * better performance and quality. 57 * </p> 58 * 59 * <p>Please note that {@link SpellCheckerService.Session#getLocale()} does not return a valid 60 * locale before {@link SpellCheckerService.Session#onCreate()} </p> 61 * 62 */ 63public abstract class SpellCheckerService extends Service { 64 private static final String TAG = SpellCheckerService.class.getSimpleName(); 65 private static final boolean DBG = false; 66 public static final String SERVICE_INTERFACE = 67 "android.service.textservice.SpellCheckerService"; 68 69 private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this); 70 71 72 /** 73 * Implement to return the implementation of the internal spell checker 74 * service interface. Subclasses should not override. 75 */ 76 @Override 77 public final IBinder onBind(final Intent intent) { 78 if (DBG) { 79 Log.w(TAG, "onBind"); 80 } 81 return mBinder; 82 } 83 84 /** 85 * Factory method to create a spell checker session impl 86 * @return SpellCheckerSessionImpl which should be overridden by a concrete implementation. 87 */ 88 public abstract Session createSession(); 89 90 /** 91 * This abstract class should be overridden by a concrete implementation of a spell checker. 92 */ 93 public static abstract class Session { 94 private InternalISpellCheckerSession mInternalSession; 95 96 /** 97 * @hide 98 */ 99 public final void setInternalISpellCheckerSession(InternalISpellCheckerSession session) { 100 mInternalSession = session; 101 } 102 103 /** 104 * This is called after the class is initialized, at which point it knows it can call 105 * getLocale() etc... 106 */ 107 public abstract void onCreate(); 108 109 /** 110 * Get suggestions for specified text in TextInfo. 111 * This function will run on the incoming IPC thread. 112 * So, this is not called on the main thread, 113 * but will be called in series on another thread. 114 * @param textInfo the text metadata 115 * @param suggestionsLimit the maximum number of suggestions to be returned 116 * @return SuggestionsInfo which contains suggestions for textInfo 117 */ 118 public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit); 119 120 /** 121 * A batch process of onGetSuggestions. 122 * This function will run on the incoming IPC thread. 123 * So, this is not called on the main thread, 124 * but will be called in series on another thread. 125 * @param textInfos an array of the text metadata 126 * @param suggestionsLimit the maximum number of suggestions to be returned 127 * @param sequentialWords true if textInfos can be treated as sequential words. 128 * @return an array of {@link SentenceSuggestionsInfo} returned by 129 * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 130 */ 131 public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos, 132 int suggestionsLimit, boolean sequentialWords) { 133 final int length = textInfos.length; 134 final SuggestionsInfo[] retval = new SuggestionsInfo[length]; 135 for (int i = 0; i < length; ++i) { 136 retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit); 137 retval[i].setCookieAndSequence( 138 textInfos[i].getCookie(), textInfos[i].getSequence()); 139 } 140 return retval; 141 } 142 143 /** 144 * The default implementation returns an array of SentenceSuggestionsInfo by simply calling 145 * onGetSuggestions(). 146 * When you override this method, make sure that suggestionsLimit is applied to suggestions 147 * that share the same start position and length. 148 * @param textInfos an array of the text metadata 149 * @param suggestionsLimit the maximum number of suggestions to be returned 150 * @return an array of {@link SentenceSuggestionsInfo} returned by 151 * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 152 */ 153 public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, 154 int suggestionsLimit) { 155 final int length = textInfos.length; 156 final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length]; 157 for (int i = 0; i < length; ++i) { 158 final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit); 159 si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); 160 final int N = textInfos[i].getText().length(); 161 retval[i] = new SentenceSuggestionsInfo( 162 new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N}); 163 } 164 return retval; 165 } 166 167 /** 168 * Request to abort all tasks executed in SpellChecker. 169 * This function will run on the incoming IPC thread. 170 * So, this is not called on the main thread, 171 * but will be called in series on another thread. 172 */ 173 public void onCancel() {} 174 175 /** 176 * Request to close this session. 177 * This function will run on the incoming IPC thread. 178 * So, this is not called on the main thread, 179 * but will be called in series on another thread. 180 */ 181 public void onClose() {} 182 183 /** 184 * @return Locale for this session 185 */ 186 public String getLocale() { 187 return mInternalSession.getLocale(); 188 } 189 190 /** 191 * @return Bundle for this session 192 */ 193 public Bundle getBundle() { 194 return mInternalSession.getBundle(); 195 } 196 } 197 198 // Preventing from exposing ISpellCheckerSession.aidl, create an internal class. 199 private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub { 200 private ISpellCheckerSessionListener mListener; 201 private final Session mSession; 202 private final String mLocale; 203 private final Bundle mBundle; 204 205 public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener, 206 Bundle bundle, Session session) { 207 mListener = listener; 208 mSession = session; 209 mLocale = locale; 210 mBundle = bundle; 211 session.setInternalISpellCheckerSession(this); 212 } 213 214 @Override 215 public void onGetSuggestionsMultiple( 216 TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 217 int pri = Process.getThreadPriority(Process.myTid()); 218 try { 219 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 220 mListener.onGetSuggestions( 221 mSession.onGetSuggestionsMultiple( 222 textInfos, suggestionsLimit, sequentialWords)); 223 } catch (RemoteException e) { 224 } finally { 225 Process.setThreadPriority(pri); 226 } 227 } 228 229 @Override 230 public void onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { 231 try { 232 mListener.onGetSentenceSuggestions( 233 mSession.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit)); 234 } catch (RemoteException e) { 235 } 236 } 237 238 @Override 239 public void onCancel() { 240 int pri = Process.getThreadPriority(Process.myTid()); 241 try { 242 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 243 mSession.onCancel(); 244 } finally { 245 Process.setThreadPriority(pri); 246 } 247 } 248 249 @Override 250 public void onClose() { 251 int pri = Process.getThreadPriority(Process.myTid()); 252 try { 253 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 254 mSession.onClose(); 255 } finally { 256 Process.setThreadPriority(pri); 257 mListener = null; 258 } 259 } 260 261 public String getLocale() { 262 return mLocale; 263 } 264 265 public Bundle getBundle() { 266 return mBundle; 267 } 268 } 269 270 private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub { 271 private final WeakReference<SpellCheckerService> mInternalServiceRef; 272 273 public SpellCheckerServiceBinder(SpellCheckerService service) { 274 mInternalServiceRef = new WeakReference<SpellCheckerService>(service); 275 } 276 277 @Override 278 public ISpellCheckerSession getISpellCheckerSession( 279 String locale, ISpellCheckerSessionListener listener, Bundle bundle) { 280 final SpellCheckerService service = mInternalServiceRef.get(); 281 if (service == null) return null; 282 final Session session = service.createSession(); 283 final InternalISpellCheckerSession internalSession = 284 new InternalISpellCheckerSession(locale, listener, bundle, session); 285 session.onCreate(); 286 return internalSession; 287 } 288 } 289} 290