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