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.view.textservice;
     18 
     19 import com.android.internal.textservice.ITextServicesManager;
     20 
     21 import android.content.Context;
     22 import android.os.Bundle;
     23 import android.os.IBinder;
     24 import android.os.RemoteException;
     25 import android.os.ServiceManager;
     26 import android.util.Log;
     27 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
     28 
     29 import java.util.Locale;
     30 
     31 /**
     32  * System API to the overall text services, which arbitrates interaction between applications
     33  * and text services. You can retrieve an instance of this interface with
     34  * {@link Context#getSystemService(String) Context.getSystemService()}.
     35  *
     36  * The user can change the current text services in Settings. And also applications can specify
     37  * the target text services.
     38  *
     39  * <h3>Architecture Overview</h3>
     40  *
     41  * <p>There are three primary parties involved in the text services
     42  * framework (TSF) architecture:</p>
     43  *
     44  * <ul>
     45  * <li> The <strong>text services manager</strong> as expressed by this class
     46  * is the central point of the system that manages interaction between all
     47  * other parts.  It is expressed as the client-side API here which exists
     48  * in each application context and communicates with a global system service
     49  * that manages the interaction across all processes.
     50  * <li> A <strong>text service</strong> implements a particular
     51  * interaction model allowing the client application to retrieve information of text.
     52  * The system binds to the current text service that is in use, causing it to be created and run.
     53  * <li> Multiple <strong>client applications</strong> arbitrate with the text service
     54  * manager for connections to text services.
     55  * </ul>
     56  *
     57  * <h3>Text services sessions</h3>
     58  * <ul>
     59  * <li>The <strong>spell checker session</strong> is one of the text services.
     60  * {@link android.view.textservice.SpellCheckerSession}</li>
     61  * </ul>
     62  *
     63  */
     64 public final class TextServicesManager {
     65     private static final String TAG = TextServicesManager.class.getSimpleName();
     66     private static final boolean DBG = false;
     67 
     68     private static TextServicesManager sInstance;
     69     private static ITextServicesManager sService;
     70 
     71     private TextServicesManager() {
     72         if (sService == null) {
     73             IBinder b = ServiceManager.getService(Context.TEXT_SERVICES_MANAGER_SERVICE);
     74             sService = ITextServicesManager.Stub.asInterface(b);
     75         }
     76     }
     77 
     78     /**
     79      * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
     80      * @hide
     81      */
     82     public static TextServicesManager getInstance() {
     83         synchronized (TextServicesManager.class) {
     84             if (sInstance != null) {
     85                 return sInstance;
     86             }
     87             sInstance = new TextServicesManager();
     88         }
     89         return sInstance;
     90     }
     91 
     92     /**
     93      * Get a spell checker session for the specified spell checker
     94      * @param locale the locale for the spell checker. If {@param locale} is null and
     95      * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
     96      * returned. If {@param locale} is not null and referToSpellCheckerLanguageSettings is true,
     97      * the locale specified in Settings will be returned only when it is same as {@param locale}.
     98      * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@param locale} is
     99      * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
    100      * selected.
    101      * @param listener a spell checker session lister for getting results from a spell checker.
    102      * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
    103      * languages in settings will be returned.
    104      * @return the spell checker session of the spell checker
    105      */
    106     public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
    107             SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
    108         if (listener == null) {
    109             throw new NullPointerException();
    110         }
    111         if (!referToSpellCheckerLanguageSettings && locale == null) {
    112             throw new IllegalArgumentException("Locale should not be null if you don't refer"
    113                     + " settings.");
    114         }
    115 
    116         if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
    117             return null;
    118         }
    119 
    120         final SpellCheckerInfo sci;
    121         try {
    122             sci = sService.getCurrentSpellChecker(null);
    123         } catch (RemoteException e) {
    124             return null;
    125         }
    126         if (sci == null) {
    127             return null;
    128         }
    129         SpellCheckerSubtype subtypeInUse = null;
    130         if (referToSpellCheckerLanguageSettings) {
    131             subtypeInUse = getCurrentSpellCheckerSubtype(true);
    132             if (subtypeInUse == null) {
    133                 return null;
    134             }
    135             if (locale != null) {
    136                 final String subtypeLocale = subtypeInUse.getLocale();
    137                 final String inputLocale = locale.toString();
    138                 if (subtypeLocale.length() < 2 || inputLocale.length() < 2
    139                         || !subtypeLocale.substring(0, 2).equals(inputLocale.substring(0, 2))) {
    140                     return null;
    141                 }
    142             }
    143         } else {
    144             final String localeStr = locale.toString();
    145             for (int i = 0; i < sci.getSubtypeCount(); ++i) {
    146                 final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
    147                 final String tempSubtypeLocale = subtype.getLocale();
    148                 if (tempSubtypeLocale.equals(localeStr)) {
    149                     subtypeInUse = subtype;
    150                     break;
    151                 } else if (localeStr.length() >= 2 && tempSubtypeLocale.length() >= 2
    152                         && localeStr.startsWith(tempSubtypeLocale)) {
    153                     subtypeInUse = subtype;
    154                 }
    155             }
    156         }
    157         if (subtypeInUse == null) {
    158             return null;
    159         }
    160         final SpellCheckerSession session = new SpellCheckerSession(sci, sService, listener);
    161         try {
    162             sService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
    163                     session.getTextServicesSessionListener(),
    164                     session.getSpellCheckerSessionListener(), bundle);
    165         } catch (RemoteException e) {
    166             return null;
    167         }
    168         return session;
    169     }
    170 
    171     /**
    172      * @hide
    173      */
    174     public SpellCheckerInfo[] getEnabledSpellCheckers() {
    175         try {
    176             final SpellCheckerInfo[] retval = sService.getEnabledSpellCheckers();
    177             if (DBG) {
    178                 Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
    179             }
    180             return retval;
    181         } catch (RemoteException e) {
    182             Log.e(TAG, "Error in getEnabledSpellCheckers: " + e);
    183             return null;
    184         }
    185     }
    186 
    187     /**
    188      * @hide
    189      */
    190     public SpellCheckerInfo getCurrentSpellChecker() {
    191         try {
    192             // Passing null as a locale for ICS
    193             return sService.getCurrentSpellChecker(null);
    194         } catch (RemoteException e) {
    195             return null;
    196         }
    197     }
    198 
    199     /**
    200      * @hide
    201      */
    202     public void setCurrentSpellChecker(SpellCheckerInfo sci) {
    203         try {
    204             if (sci == null) {
    205                 throw new NullPointerException("SpellCheckerInfo is null.");
    206             }
    207             sService.setCurrentSpellChecker(null, sci.getId());
    208         } catch (RemoteException e) {
    209             Log.e(TAG, "Error in setCurrentSpellChecker: " + e);
    210         }
    211     }
    212 
    213     /**
    214      * @hide
    215      */
    216     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
    217             boolean allowImplicitlySelectedSubtype) {
    218         try {
    219             // Passing null as a locale for ICS
    220             return sService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
    221         } catch (RemoteException e) {
    222             Log.e(TAG, "Error in getCurrentSpellCheckerSubtype: " + e);
    223             return null;
    224         }
    225     }
    226 
    227     /**
    228      * @hide
    229      */
    230     public void setSpellCheckerSubtype(SpellCheckerSubtype subtype) {
    231         try {
    232             final int hashCode;
    233             if (subtype == null) {
    234                 hashCode = 0;
    235             } else {
    236                 hashCode = subtype.hashCode();
    237             }
    238             sService.setCurrentSpellCheckerSubtype(null, hashCode);
    239         } catch (RemoteException e) {
    240             Log.e(TAG, "Error in setSpellCheckerSubtype:" + e);
    241         }
    242     }
    243 
    244     /**
    245      * @hide
    246      */
    247     public void setSpellCheckerEnabled(boolean enabled) {
    248         try {
    249             sService.setSpellCheckerEnabled(enabled);
    250         } catch (RemoteException e) {
    251             Log.e(TAG, "Error in setSpellCheckerEnabled:" + e);
    252         }
    253     }
    254 
    255     /**
    256      * @hide
    257      */
    258     public boolean isSpellCheckerEnabled() {
    259         try {
    260             return sService.isSpellCheckerEnabled();
    261         } catch (RemoteException e) {
    262             Log.e(TAG, "Error in isSpellCheckerEnabled:" + e);
    263             return false;
    264         }
    265     }
    266 }
    267