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