blob: 2b8a4582896e728f119ca5e3d5a1da7050a9c6e3 [file] [log] [blame]
satok988323c2011-06-22 16:38:13 +09001/*
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;
satok53578062011-08-03 16:08:59 +090025import android.os.Bundle;
satok988323c2011-06-22 16:38:13 +090026import android.os.IBinder;
Dianne Hackborn33b8ee52011-12-13 15:08:40 -080027import android.os.Process;
satok988323c2011-06-22 16:38:13 +090028import android.os.RemoteException;
satok6be6d752011-07-28 20:40:38 +090029import android.util.Log;
satok988323c2011-06-22 16:38:13 +090030import 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.
satok44b75032011-10-14 14:48:59 +090039 *
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 *
satok988323c2011-06-22 16:38:13 +090061 */
62public abstract class SpellCheckerService extends Service {
63 private static final String TAG = SpellCheckerService.class.getSimpleName();
satok6be6d752011-07-28 20:40:38 +090064 private static final boolean DBG = false;
satok142d7572011-07-25 11:01:49 +090065 public static final String SERVICE_INTERFACE =
66 "android.service.textservice.SpellCheckerService";
satok988323c2011-06-22 16:38:13 +090067
68 private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this);
69
satok988323c2011-06-22 16:38:13 +090070
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) {
satok6be6d752011-07-28 20:40:38 +090077 if (DBG) {
78 Log.w(TAG, "onBind");
79 }
satok988323c2011-06-22 16:38:13 +090080 return mBinder;
81 }
82
satok53578062011-08-03 16:08:59 +090083 /**
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();
satok988323c2011-06-22 16:38:13 +090088
satok53578062011-08-03 16:08:59 +090089 /**
90 * This abstract class should be overridden by a concrete implementation of a spell checker.
91 */
satok117999d2011-09-02 17:55:43 +090092 public static abstract class Session {
satok53578062011-08-03 16:08:59 +090093 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
satok44b75032011-10-14 14:48:59 +0900115 * @return SuggestionsInfo which contains suggestions for textInfo
satok53578062011-08-03 16:08:59 +0900116 */
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.
satok44b75032011-10-14 14:48:59 +0900127 * @return an array of SuggestionsInfo of onGetSuggestions
satok53578062011-08-03 16:08:59 +0900128 */
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 /**
satok0dc1f642011-11-18 11:27:10 +0900142 * @hide
143 * The default implementation returns an array of SuggestionsInfo by simply calling
144 * onGetSuggestions().
145 * When you override this method, make sure that suggestionsLimit is applied to suggestions
146 * that share the same start position and length.
147 */
148 public SuggestionsInfo[] onGetSuggestionsMultipleForSentence(TextInfo[] textInfos,
149 int suggestionsLimit) {
150 final int length = textInfos.length;
151 final SuggestionsInfo[] retval = new SuggestionsInfo[length];
152 for (int i = 0; i < length; ++i) {
153 retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
154 retval[i].setCookieAndSequence(
155 textInfos[i].getCookie(), textInfos[i].getSequence());
156 }
157 return retval;
158 }
159
160 /**
satok53578062011-08-03 16:08:59 +0900161 * Request to abort all tasks executed in SpellChecker.
162 * This function will run on the incoming IPC thread.
163 * So, this is not called on the main thread,
164 * but will be called in series on another thread.
165 */
166 public void onCancel() {}
167
168 /**
satok74061ff2011-11-02 11:20:33 +0900169 * Request to close this session.
170 * This function will run on the incoming IPC thread.
171 * So, this is not called on the main thread,
172 * but will be called in series on another thread.
173 */
174 public void onClose() {}
175
176 /**
satok53578062011-08-03 16:08:59 +0900177 * @return Locale for this session
178 */
179 public String getLocale() {
180 return mInternalSession.getLocale();
181 }
182
183 /**
184 * @return Bundle for this session
185 */
186 public Bundle getBundle() {
187 return mInternalSession.getBundle();
188 }
189 }
190
191 // Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
192 private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub {
satok74061ff2011-11-02 11:20:33 +0900193 private ISpellCheckerSessionListener mListener;
satok53578062011-08-03 16:08:59 +0900194 private final Session mSession;
195 private final String mLocale;
196 private final Bundle mBundle;
197
198 public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener,
199 Bundle bundle, Session session) {
satok988323c2011-06-22 16:38:13 +0900200 mListener = listener;
satok53578062011-08-03 16:08:59 +0900201 mSession = session;
202 mLocale = locale;
203 mBundle = bundle;
204 session.setInternalISpellCheckerSession(this);
satok988323c2011-06-22 16:38:13 +0900205 }
206
207 @Override
satok53578062011-08-03 16:08:59 +0900208 public void onGetSuggestionsMultiple(
satok988323c2011-06-22 16:38:13 +0900209 TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
Dianne Hackborn33b8ee52011-12-13 15:08:40 -0800210 int pri = Process.getThreadPriority(Process.myTid());
satok988323c2011-06-22 16:38:13 +0900211 try {
Dianne Hackborn33b8ee52011-12-13 15:08:40 -0800212 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
satok988323c2011-06-22 16:38:13 +0900213 mListener.onGetSuggestions(
satok53578062011-08-03 16:08:59 +0900214 mSession.onGetSuggestionsMultiple(
215 textInfos, suggestionsLimit, sequentialWords));
satok988323c2011-06-22 16:38:13 +0900216 } catch (RemoteException e) {
Dianne Hackborn33b8ee52011-12-13 15:08:40 -0800217 } finally {
218 Process.setThreadPriority(pri);
satok988323c2011-06-22 16:38:13 +0900219 }
220 }
221
222 @Override
satok0dc1f642011-11-18 11:27:10 +0900223 public void onGetSuggestionsMultipleForSentence(
224 TextInfo[] textInfos, int suggestionsLimit) {
225 try {
226 mListener.onGetSuggestionsForSentence(
227 mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit));
228 } catch (RemoteException e) {
229 }
230 }
231
232 @Override
satok53578062011-08-03 16:08:59 +0900233 public void onCancel() {
Dianne Hackborn33b8ee52011-12-13 15:08:40 -0800234 int pri = Process.getThreadPriority(Process.myTid());
235 try {
236 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
237 mSession.onCancel();
238 } finally {
239 Process.setThreadPriority(pri);
240 }
satok53578062011-08-03 16:08:59 +0900241 }
242
satok74061ff2011-11-02 11:20:33 +0900243 @Override
244 public void onClose() {
Dianne Hackborn33b8ee52011-12-13 15:08:40 -0800245 int pri = Process.getThreadPriority(Process.myTid());
246 try {
247 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
248 mSession.onClose();
249 } finally {
250 Process.setThreadPriority(pri);
251 mListener = null;
252 }
satok74061ff2011-11-02 11:20:33 +0900253 }
254
satok53578062011-08-03 16:08:59 +0900255 public String getLocale() {
256 return mLocale;
257 }
258
259 public Bundle getBundle() {
260 return mBundle;
satok988323c2011-06-22 16:38:13 +0900261 }
262 }
263
264 private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub {
265 private final WeakReference<SpellCheckerService> mInternalServiceRef;
266
267 public SpellCheckerServiceBinder(SpellCheckerService service) {
268 mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
269 }
270
271 @Override
272 public ISpellCheckerSession getISpellCheckerSession(
satok53578062011-08-03 16:08:59 +0900273 String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
satok988323c2011-06-22 16:38:13 +0900274 final SpellCheckerService service = mInternalServiceRef.get();
275 if (service == null) return null;
satok53578062011-08-03 16:08:59 +0900276 final Session session = service.createSession();
277 final InternalISpellCheckerSession internalSession =
278 new InternalISpellCheckerSession(locale, listener, bundle, session);
279 session.onCreate();
280 return internalSession;
satok988323c2011-06-22 16:38:13 +0900281 }
282 }
283}