SpellCheckerService.java revision 0dc1f648a09b46c45190ba1ce7daecf7fada4347
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.RemoteException; 28import android.util.Log; 29import android.view.textservice.SuggestionsInfo; 30import android.view.textservice.TextInfo; 31 32import java.lang.ref.WeakReference; 33 34/** 35 * SpellCheckerService provides an abstract base class for a spell checker. 36 * This class combines a service to the system with the spell checker service interface that 37 * spell checker must implement. 38 * 39 * <p>In addition to the normal Service lifecycle methods, this class 40 * introduces a new specific callback that subclasses should override 41 * {@link #createSession()} to provide a spell checker session that is corresponding 42 * to requested language and so on. The spell checker session returned by this method 43 * should extend {@link SpellCheckerService.Session}. 44 * </p> 45 * 46 * <h3>Returning spell check results</h3> 47 * 48 * <p>{@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} 49 * should return spell check results. 50 * It receives {@link android.view.textservice.TextInfo} and returns 51 * {@link android.view.textservice.SuggestionsInfo} for the input. 52 * You may want to override 53 * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} for 54 * better performance and quality. 55 * </p> 56 * 57 * <p>Please note that {@link SpellCheckerService.Session#getLocale()} does not return a valid 58 * locale before {@link SpellCheckerService.Session#onCreate()} </p> 59 * 60 */ 61public abstract class SpellCheckerService extends Service { 62 private static final String TAG = SpellCheckerService.class.getSimpleName(); 63 private static final boolean DBG = false; 64 public static final String SERVICE_INTERFACE = 65 "android.service.textservice.SpellCheckerService"; 66 67 private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this); 68 69 70 /** 71 * Implement to return the implementation of the internal spell checker 72 * service interface. Subclasses should not override. 73 */ 74 @Override 75 public final IBinder onBind(final Intent intent) { 76 if (DBG) { 77 Log.w(TAG, "onBind"); 78 } 79 return mBinder; 80 } 81 82 /** 83 * Factory method to create a spell checker session impl 84 * @return SpellCheckerSessionImpl which should be overridden by a concrete implementation. 85 */ 86 public abstract Session createSession(); 87 88 /** 89 * This abstract class should be overridden by a concrete implementation of a spell checker. 90 */ 91 public static abstract class Session { 92 private InternalISpellCheckerSession mInternalSession; 93 94 /** 95 * @hide 96 */ 97 public final void setInternalISpellCheckerSession(InternalISpellCheckerSession session) { 98 mInternalSession = session; 99 } 100 101 /** 102 * This is called after the class is initialized, at which point it knows it can call 103 * getLocale() etc... 104 */ 105 public abstract void onCreate(); 106 107 /** 108 * Get suggestions for specified text in TextInfo. 109 * This function will run on the incoming IPC thread. 110 * So, this is not called on the main thread, 111 * but will be called in series on another thread. 112 * @param textInfo the text metadata 113 * @param suggestionsLimit the number of limit of suggestions returned 114 * @return SuggestionsInfo which contains suggestions for textInfo 115 */ 116 public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit); 117 118 /** 119 * A batch process of onGetSuggestions. 120 * This function will run on the incoming IPC thread. 121 * So, this is not called on the main thread, 122 * but will be called in series on another thread. 123 * @param textInfos an array of the text metadata 124 * @param suggestionsLimit the number of limit of suggestions returned 125 * @param sequentialWords true if textInfos can be treated as sequential words. 126 * @return an array of SuggestionsInfo of onGetSuggestions 127 */ 128 public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos, 129 int suggestionsLimit, boolean sequentialWords) { 130 final int length = textInfos.length; 131 final SuggestionsInfo[] retval = new SuggestionsInfo[length]; 132 for (int i = 0; i < length; ++i) { 133 retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit); 134 retval[i].setCookieAndSequence( 135 textInfos[i].getCookie(), textInfos[i].getSequence()); 136 } 137 return retval; 138 } 139 140 /** 141 * @hide 142 * The default implementation returns an array of SuggestionsInfo by simply calling 143 * onGetSuggestions(). 144 * When you override this method, make sure that suggestionsLimit is applied to suggestions 145 * that share the same start position and length. 146 */ 147 public SuggestionsInfo[] onGetSuggestionsMultipleForSentence(TextInfo[] textInfos, 148 int suggestionsLimit) { 149 final int length = textInfos.length; 150 final SuggestionsInfo[] retval = new SuggestionsInfo[length]; 151 for (int i = 0; i < length; ++i) { 152 retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit); 153 retval[i].setCookieAndSequence( 154 textInfos[i].getCookie(), textInfos[i].getSequence()); 155 } 156 return retval; 157 } 158 159 /** 160 * Request to abort all tasks executed in SpellChecker. 161 * This function will run on the incoming IPC thread. 162 * So, this is not called on the main thread, 163 * but will be called in series on another thread. 164 */ 165 public void onCancel() {} 166 167 /** 168 * Request to close this session. 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 onClose() {} 174 175 /** 176 * @return Locale for this session 177 */ 178 public String getLocale() { 179 return mInternalSession.getLocale(); 180 } 181 182 /** 183 * @return Bundle for this session 184 */ 185 public Bundle getBundle() { 186 return mInternalSession.getBundle(); 187 } 188 } 189 190 // Preventing from exposing ISpellCheckerSession.aidl, create an internal class. 191 private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub { 192 private ISpellCheckerSessionListener mListener; 193 private final Session mSession; 194 private final String mLocale; 195 private final Bundle mBundle; 196 197 public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener, 198 Bundle bundle, Session session) { 199 mListener = listener; 200 mSession = session; 201 mLocale = locale; 202 mBundle = bundle; 203 session.setInternalISpellCheckerSession(this); 204 } 205 206 @Override 207 public void onGetSuggestionsMultiple( 208 TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { 209 try { 210 mListener.onGetSuggestions( 211 mSession.onGetSuggestionsMultiple( 212 textInfos, suggestionsLimit, sequentialWords)); 213 } catch (RemoteException e) { 214 } 215 } 216 217 @Override 218 public void onGetSuggestionsMultipleForSentence( 219 TextInfo[] textInfos, int suggestionsLimit) { 220 try { 221 mListener.onGetSuggestionsForSentence( 222 mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit)); 223 } catch (RemoteException e) { 224 } 225 } 226 227 @Override 228 public void onCancel() { 229 mSession.onCancel(); 230 } 231 232 @Override 233 public void onClose() { 234 mSession.onClose(); 235 mListener = null; 236 } 237 238 public String getLocale() { 239 return mLocale; 240 } 241 242 public Bundle getBundle() { 243 return mBundle; 244 } 245 } 246 247 private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub { 248 private final WeakReference<SpellCheckerService> mInternalServiceRef; 249 250 public SpellCheckerServiceBinder(SpellCheckerService service) { 251 mInternalServiceRef = new WeakReference<SpellCheckerService>(service); 252 } 253 254 @Override 255 public ISpellCheckerSession getISpellCheckerSession( 256 String locale, ISpellCheckerSessionListener listener, Bundle bundle) { 257 final SpellCheckerService service = mInternalServiceRef.get(); 258 if (service == null) return null; 259 final Session session = service.createSession(); 260 final InternalISpellCheckerSession internalSession = 261 new InternalISpellCheckerSession(locale, listener, bundle, session); 262 session.onCreate(); 263 return internalSession; 264 } 265 } 266} 267