SpellCheckerService.java revision 431ea84e1fde20139b748a4818c44e85a715e155
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 * Get sentence suggestions for specified texts in an array of TextInfo. 145 * The default implementation returns an array of SentenceSuggestionsInfo by simply 146 * calling onGetSuggestions. 147 * This function will run on the incoming IPC thread. 148 * So, this is not called on the main thread, 149 * but will be called in series on another thread. 150 * When you override this method, make sure that suggestionsLimit is applied to suggestions 151 * that share the same start position and length. 152 * @param textInfos an array of the text metadata 153 * @param suggestionsLimit the maximum number of suggestions to be returned 154 * @return an array of {@link SentenceSuggestionsInfo} returned by 155 * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 156 */ 157 public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, 158 int suggestionsLimit) { 159 final int length = textInfos.length; 160 final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length]; 161 for (int i = 0; i < length; ++i) { 162 final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit); 163 si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); 164 final int N = textInfos[i].getText().length(); 165 retval[i] = new SentenceSuggestionsInfo( 166 new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N}); 167 } 168 return retval; 169 } 170 171 /** 172 * Request to abort all tasks executed in SpellChecker. 173 * This function will run on the incoming IPC thread. 174 * So, this is not called on the main thread, 175 * but will be called in series on another thread. 176 */ 177 public void onCancel() {} 178 179 /** 180 * Request to close this session. 181 * This function will run on the incoming IPC thread. 182 * So, this is not called on the main thread, 183 * but will be called in series on another thread. 184 */ 185 public void onClose() {} 186 187 /** 188 * @return Locale for this session 189 */ 190 public String getLocale() { 191 return mInternalSession.getLocale(); 192 } 193 194 /** 195 * @return Bundle for this session 196 */ 197 public Bundle getBundle() { 198 return mInternalSession.getBundle(); 199 } 200 } 201 202 // Preventing from exposing ISpellCheckerSession.aidl, create an internal class. 203 private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub { 204 private ISpellCheckerSessionListener mListener; 205 private final Session mSession; 206 private final String mLocale; 207 private final Bundle mBundle; 208 209 public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener, 210 Bundle bundle, Session session) { 211 mListener = listener; 212 mSession = session; 213 mLocale = locale; 214 mBundle = bundle; 215 session.setInternalISpellCheckerSession(this); 216 } 217 218 @Override 219 public void onGetSuggestionsMultiple( 220 TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 221 int pri = Process.getThreadPriority(Process.myTid()); 222 try { 223 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 224 mListener.onGetSuggestions( 225 mSession.onGetSuggestionsMultiple( 226 textInfos, suggestionsLimit, sequentialWords)); 227 } catch (RemoteException e) { 228 } finally { 229 Process.setThreadPriority(pri); 230 } 231 } 232 233 @Override 234 public void onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { 235 try { 236 mListener.onGetSentenceSuggestions( 237 mSession.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit)); 238 } catch (RemoteException e) { 239 } 240 } 241 242 @Override 243 public void onCancel() { 244 int pri = Process.getThreadPriority(Process.myTid()); 245 try { 246 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 247 mSession.onCancel(); 248 } finally { 249 Process.setThreadPriority(pri); 250 } 251 } 252 253 @Override 254 public void onClose() { 255 int pri = Process.getThreadPriority(Process.myTid()); 256 try { 257 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 258 mSession.onClose(); 259 } finally { 260 Process.setThreadPriority(pri); 261 mListener = null; 262 } 263 } 264 265 public String getLocale() { 266 return mLocale; 267 } 268 269 public Bundle getBundle() { 270 return mBundle; 271 } 272 } 273 274 private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub { 275 private final WeakReference<SpellCheckerService> mInternalServiceRef; 276 277 public SpellCheckerServiceBinder(SpellCheckerService service) { 278 mInternalServiceRef = new WeakReference<SpellCheckerService>(service); 279 } 280 281 @Override 282 public ISpellCheckerSession getISpellCheckerSession( 283 String locale, ISpellCheckerSessionListener listener, Bundle bundle) { 284 final SpellCheckerService service = mInternalServiceRef.get(); 285 if (service == null) return null; 286 final Session session = service.createSession(); 287 final InternalISpellCheckerSession internalSession = 288 new InternalISpellCheckerSession(locale, listener, bundle, session); 289 session.onCreate(); 290 return internalSession; 291 } 292 } 293} 294