Home | History | Annotate | Download | only in textservice
      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 
     17 package android.service.textservice;
     18 
     19 import com.android.internal.textservice.ISpellCheckerService;
     20 import com.android.internal.textservice.ISpellCheckerSession;
     21 import com.android.internal.textservice.ISpellCheckerSessionListener;
     22 
     23 import android.app.Service;
     24 import android.content.Intent;
     25 import android.os.Bundle;
     26 import android.os.IBinder;
     27 import android.os.Process;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 import android.view.textservice.SuggestionsInfo;
     31 import android.view.textservice.TextInfo;
     32 
     33 import 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  */
     62 public 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