SpellCheckerService.java revision 74061ff90453c79ddbde468f630a41425da07710
1988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/*
2988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Copyright (C) 2011 The Android Open Source Project
3988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
4988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * use this file except in compliance with the License. You may obtain a copy of
6988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * the License at
7988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
8988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * http://www.apache.org/licenses/LICENSE-2.0
9988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
10988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Unless required by applicable law or agreed to in writing, software
11988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * License for the specific language governing permissions and limitations under
14988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * the License.
15988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */
16988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
17988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpackage android.service.textservice;
18988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
19988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerService;
20988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSession;
21988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSessionListener;
22988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
23988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.app.Service;
24988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Intent;
255357806980269d846a15c845a6fcc0384fb18860satokimport android.os.Bundle;
26988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.IBinder;
27988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.RemoteException;
286be6d7548fb7c29a4d46dc985318ab2adf69f95fsatokimport android.util.Log;
29988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.SuggestionsInfo;
30988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.TextInfo;
31988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
32988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.lang.ref.WeakReference;
33988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
34988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/**
35988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * SpellCheckerService provides an abstract base class for a spell checker.
36988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * This class combines a service to the system with the spell checker service interface that
37988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * spell checker must implement.
3844b75030931d9c65c9e495a86d11d71da59b4429satok *
3944b75030931d9c65c9e495a86d11d71da59b4429satok * <p>In addition to the normal Service lifecycle methods, this class
4044b75030931d9c65c9e495a86d11d71da59b4429satok * introduces a new specific callback that subclasses should override
4144b75030931d9c65c9e495a86d11d71da59b4429satok * {@link #createSession()} to provide a spell checker session that is corresponding
4244b75030931d9c65c9e495a86d11d71da59b4429satok * to requested language and so on. The spell checker session returned by this method
4344b75030931d9c65c9e495a86d11d71da59b4429satok * should extend {@link SpellCheckerService.Session}.
4444b75030931d9c65c9e495a86d11d71da59b4429satok * </p>
4544b75030931d9c65c9e495a86d11d71da59b4429satok *
4644b75030931d9c65c9e495a86d11d71da59b4429satok * <h3>Returning spell check results</h3>
4744b75030931d9c65c9e495a86d11d71da59b4429satok *
4844b75030931d9c65c9e495a86d11d71da59b4429satok * <p>{@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
4944b75030931d9c65c9e495a86d11d71da59b4429satok * should return spell check results.
5044b75030931d9c65c9e495a86d11d71da59b4429satok * It receives {@link android.view.textservice.TextInfo} and returns
5144b75030931d9c65c9e495a86d11d71da59b4429satok * {@link android.view.textservice.SuggestionsInfo} for the input.
5244b75030931d9c65c9e495a86d11d71da59b4429satok * You may want to override
5344b75030931d9c65c9e495a86d11d71da59b4429satok * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} for
5444b75030931d9c65c9e495a86d11d71da59b4429satok * better performance and quality.
5544b75030931d9c65c9e495a86d11d71da59b4429satok * </p>
5644b75030931d9c65c9e495a86d11d71da59b4429satok *
5744b75030931d9c65c9e495a86d11d71da59b4429satok * <p>Please note that {@link SpellCheckerService.Session#getLocale()} does not return a valid
5844b75030931d9c65c9e495a86d11d71da59b4429satok * locale before {@link SpellCheckerService.Session#onCreate()} </p>
5944b75030931d9c65c9e495a86d11d71da59b4429satok *
60988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */
61988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpublic abstract class SpellCheckerService extends Service {
62988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static final String TAG = SpellCheckerService.class.getSimpleName();
636be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok    private static final boolean DBG = false;
64142d7575b52d03d46246e3b142e22ebc32d45a84satok    public static final String SERVICE_INTERFACE =
65142d7575b52d03d46246e3b142e22ebc32d45a84satok            "android.service.textservice.SpellCheckerService";
66988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
67988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this);
68988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
69988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
70988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
715357806980269d846a15c845a6fcc0384fb18860satok     * Implement to return the implementation of the internal spell checker
725357806980269d846a15c845a6fcc0384fb18860satok     * service interface. Subclasses should not override.
73988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
745357806980269d846a15c845a6fcc0384fb18860satok    @Override
755357806980269d846a15c845a6fcc0384fb18860satok    public final IBinder onBind(final Intent intent) {
765357806980269d846a15c845a6fcc0384fb18860satok        if (DBG) {
775357806980269d846a15c845a6fcc0384fb18860satok            Log.w(TAG, "onBind");
78988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
795357806980269d846a15c845a6fcc0384fb18860satok        return mBinder;
80988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
81988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
82988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
835357806980269d846a15c845a6fcc0384fb18860satok     * Factory method to create a spell checker session impl
845357806980269d846a15c845a6fcc0384fb18860satok     * @return SpellCheckerSessionImpl which should be overridden by a concrete implementation.
85988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
865357806980269d846a15c845a6fcc0384fb18860satok    public abstract Session createSession();
87988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
88988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    /**
895357806980269d846a15c845a6fcc0384fb18860satok     * This abstract class should be overridden by a concrete implementation of a spell checker.
90988323c57bd25a58f05dfa492d9b9c8ab62c5153satok     */
91117999d1f44ec3423369385495ae207898b7b73esatok    public static abstract class Session {
925357806980269d846a15c845a6fcc0384fb18860satok        private InternalISpellCheckerSession mInternalSession;
935357806980269d846a15c845a6fcc0384fb18860satok
945357806980269d846a15c845a6fcc0384fb18860satok        /**
955357806980269d846a15c845a6fcc0384fb18860satok         * @hide
965357806980269d846a15c845a6fcc0384fb18860satok         */
975357806980269d846a15c845a6fcc0384fb18860satok        public final void setInternalISpellCheckerSession(InternalISpellCheckerSession session) {
985357806980269d846a15c845a6fcc0384fb18860satok            mInternalSession = session;
995357806980269d846a15c845a6fcc0384fb18860satok        }
1005357806980269d846a15c845a6fcc0384fb18860satok
1015357806980269d846a15c845a6fcc0384fb18860satok        /**
1025357806980269d846a15c845a6fcc0384fb18860satok         * This is called after the class is initialized, at which point it knows it can call
1035357806980269d846a15c845a6fcc0384fb18860satok         * getLocale() etc...
1045357806980269d846a15c845a6fcc0384fb18860satok         */
1055357806980269d846a15c845a6fcc0384fb18860satok        public abstract void onCreate();
1065357806980269d846a15c845a6fcc0384fb18860satok
1075357806980269d846a15c845a6fcc0384fb18860satok        /**
1085357806980269d846a15c845a6fcc0384fb18860satok         * Get suggestions for specified text in TextInfo.
1095357806980269d846a15c845a6fcc0384fb18860satok         * This function will run on the incoming IPC thread.
1105357806980269d846a15c845a6fcc0384fb18860satok         * So, this is not called on the main thread,
1115357806980269d846a15c845a6fcc0384fb18860satok         * but will be called in series on another thread.
1125357806980269d846a15c845a6fcc0384fb18860satok         * @param textInfo the text metadata
1135357806980269d846a15c845a6fcc0384fb18860satok         * @param suggestionsLimit the number of limit of suggestions returned
11444b75030931d9c65c9e495a86d11d71da59b4429satok         * @return SuggestionsInfo which contains suggestions for textInfo
1155357806980269d846a15c845a6fcc0384fb18860satok         */
1165357806980269d846a15c845a6fcc0384fb18860satok        public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit);
1175357806980269d846a15c845a6fcc0384fb18860satok
1185357806980269d846a15c845a6fcc0384fb18860satok        /**
1195357806980269d846a15c845a6fcc0384fb18860satok         * A batch process of onGetSuggestions.
1205357806980269d846a15c845a6fcc0384fb18860satok         * This function will run on the incoming IPC thread.
1215357806980269d846a15c845a6fcc0384fb18860satok         * So, this is not called on the main thread,
1225357806980269d846a15c845a6fcc0384fb18860satok         * but will be called in series on another thread.
1235357806980269d846a15c845a6fcc0384fb18860satok         * @param textInfos an array of the text metadata
1245357806980269d846a15c845a6fcc0384fb18860satok         * @param suggestionsLimit the number of limit of suggestions returned
1255357806980269d846a15c845a6fcc0384fb18860satok         * @param sequentialWords true if textInfos can be treated as sequential words.
12644b75030931d9c65c9e495a86d11d71da59b4429satok         * @return an array of SuggestionsInfo of onGetSuggestions
1275357806980269d846a15c845a6fcc0384fb18860satok         */
1285357806980269d846a15c845a6fcc0384fb18860satok        public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
1295357806980269d846a15c845a6fcc0384fb18860satok                int suggestionsLimit, boolean sequentialWords) {
1305357806980269d846a15c845a6fcc0384fb18860satok            final int length = textInfos.length;
1315357806980269d846a15c845a6fcc0384fb18860satok            final SuggestionsInfo[] retval = new SuggestionsInfo[length];
1325357806980269d846a15c845a6fcc0384fb18860satok            for (int i = 0; i < length; ++i) {
1335357806980269d846a15c845a6fcc0384fb18860satok                retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
1345357806980269d846a15c845a6fcc0384fb18860satok                retval[i].setCookieAndSequence(
1355357806980269d846a15c845a6fcc0384fb18860satok                        textInfos[i].getCookie(), textInfos[i].getSequence());
1365357806980269d846a15c845a6fcc0384fb18860satok            }
1375357806980269d846a15c845a6fcc0384fb18860satok            return retval;
1385357806980269d846a15c845a6fcc0384fb18860satok        }
1395357806980269d846a15c845a6fcc0384fb18860satok
1405357806980269d846a15c845a6fcc0384fb18860satok        /**
1415357806980269d846a15c845a6fcc0384fb18860satok         * Request to abort all tasks executed in SpellChecker.
1425357806980269d846a15c845a6fcc0384fb18860satok         * This function will run on the incoming IPC thread.
1435357806980269d846a15c845a6fcc0384fb18860satok         * So, this is not called on the main thread,
1445357806980269d846a15c845a6fcc0384fb18860satok         * but will be called in series on another thread.
1455357806980269d846a15c845a6fcc0384fb18860satok         */
1465357806980269d846a15c845a6fcc0384fb18860satok        public void onCancel() {}
1475357806980269d846a15c845a6fcc0384fb18860satok
1485357806980269d846a15c845a6fcc0384fb18860satok        /**
14974061ff90453c79ddbde468f630a41425da07710satok         * Request to close this session.
15074061ff90453c79ddbde468f630a41425da07710satok         * This function will run on the incoming IPC thread.
15174061ff90453c79ddbde468f630a41425da07710satok         * So, this is not called on the main thread,
15274061ff90453c79ddbde468f630a41425da07710satok         * but will be called in series on another thread.
15374061ff90453c79ddbde468f630a41425da07710satok         */
15474061ff90453c79ddbde468f630a41425da07710satok        public void onClose() {}
15574061ff90453c79ddbde468f630a41425da07710satok
15674061ff90453c79ddbde468f630a41425da07710satok        /**
1575357806980269d846a15c845a6fcc0384fb18860satok         * @return Locale for this session
1585357806980269d846a15c845a6fcc0384fb18860satok         */
1595357806980269d846a15c845a6fcc0384fb18860satok        public String getLocale() {
1605357806980269d846a15c845a6fcc0384fb18860satok            return mInternalSession.getLocale();
1615357806980269d846a15c845a6fcc0384fb18860satok        }
1625357806980269d846a15c845a6fcc0384fb18860satok
1635357806980269d846a15c845a6fcc0384fb18860satok        /**
1645357806980269d846a15c845a6fcc0384fb18860satok         * @return Bundle for this session
1655357806980269d846a15c845a6fcc0384fb18860satok         */
1665357806980269d846a15c845a6fcc0384fb18860satok        public Bundle getBundle() {
1675357806980269d846a15c845a6fcc0384fb18860satok            return mInternalSession.getBundle();
1686be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        }
169988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
170988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
1715357806980269d846a15c845a6fcc0384fb18860satok    // Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
1725357806980269d846a15c845a6fcc0384fb18860satok    private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub {
17374061ff90453c79ddbde468f630a41425da07710satok        private ISpellCheckerSessionListener mListener;
1745357806980269d846a15c845a6fcc0384fb18860satok        private final Session mSession;
1755357806980269d846a15c845a6fcc0384fb18860satok        private final String mLocale;
1765357806980269d846a15c845a6fcc0384fb18860satok        private final Bundle mBundle;
177988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
1785357806980269d846a15c845a6fcc0384fb18860satok        public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener,
1795357806980269d846a15c845a6fcc0384fb18860satok                Bundle bundle, Session session) {
180988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mListener = listener;
1815357806980269d846a15c845a6fcc0384fb18860satok            mSession = session;
1825357806980269d846a15c845a6fcc0384fb18860satok            mLocale = locale;
1835357806980269d846a15c845a6fcc0384fb18860satok            mBundle = bundle;
1845357806980269d846a15c845a6fcc0384fb18860satok            session.setInternalISpellCheckerSession(this);
185988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
186988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
187988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
1885357806980269d846a15c845a6fcc0384fb18860satok        public void onGetSuggestionsMultiple(
189988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
190988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            try {
191988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                mListener.onGetSuggestions(
1925357806980269d846a15c845a6fcc0384fb18860satok                        mSession.onGetSuggestionsMultiple(
1935357806980269d846a15c845a6fcc0384fb18860satok                                textInfos, suggestionsLimit, sequentialWords));
194988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            } catch (RemoteException e) {
195988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
196988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
197988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
198988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
1995357806980269d846a15c845a6fcc0384fb18860satok        public void onCancel() {
2005357806980269d846a15c845a6fcc0384fb18860satok            mSession.onCancel();
2015357806980269d846a15c845a6fcc0384fb18860satok        }
2025357806980269d846a15c845a6fcc0384fb18860satok
20374061ff90453c79ddbde468f630a41425da07710satok        @Override
20474061ff90453c79ddbde468f630a41425da07710satok        public void onClose() {
20574061ff90453c79ddbde468f630a41425da07710satok            mSession.onClose();
20674061ff90453c79ddbde468f630a41425da07710satok            mListener = null;
20774061ff90453c79ddbde468f630a41425da07710satok        }
20874061ff90453c79ddbde468f630a41425da07710satok
2095357806980269d846a15c845a6fcc0384fb18860satok        public String getLocale() {
2105357806980269d846a15c845a6fcc0384fb18860satok            return mLocale;
2115357806980269d846a15c845a6fcc0384fb18860satok        }
2125357806980269d846a15c845a6fcc0384fb18860satok
2135357806980269d846a15c845a6fcc0384fb18860satok        public Bundle getBundle() {
2145357806980269d846a15c845a6fcc0384fb18860satok            return mBundle;
215988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
216988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
217988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
218988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub {
219988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private final WeakReference<SpellCheckerService> mInternalServiceRef;
220988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
221988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public SpellCheckerServiceBinder(SpellCheckerService service) {
222988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
223988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
224988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
225988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
226988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public ISpellCheckerSession getISpellCheckerSession(
2275357806980269d846a15c845a6fcc0384fb18860satok                String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
228988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            final SpellCheckerService service = mInternalServiceRef.get();
229988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            if (service == null) return null;
2305357806980269d846a15c845a6fcc0384fb18860satok            final Session session = service.createSession();
2315357806980269d846a15c845a6fcc0384fb18860satok            final InternalISpellCheckerSession internalSession =
2325357806980269d846a15c845a6fcc0384fb18860satok                    new InternalISpellCheckerSession(locale, listener, bundle, session);
2335357806980269d846a15c845a6fcc0384fb18860satok            session.onCreate();
2345357806980269d846a15c845a6fcc0384fb18860satok            return internalSession;
235988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
236988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
237988323c57bd25a58f05dfa492d9b9c8ab62c5153satok}
238