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 {@code locale} is null and
     95      * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
     96      * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
     97      * the locale specified in Settings will be returned only when it is same as {@code locale}.
     98      * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code 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(
    161                 sci, sService, listener, subtypeInUse);
    162         try {
    163             sService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
    164                     session.getTextServicesSessionListener(),
    165                     session.getSpellCheckerSessionListener(), bundle);
    166         } catch (RemoteException e) {
    167             return null;
    168         }
    169         return session;
    170     }
    171 
    172     /**
    173      * @hide
    174      */
    175     public SpellCheckerInfo[] getEnabledSpellCheckers() {
    176         try {
    177             final SpellCheckerInfo[] retval = sService.getEnabledSpellCheckers();
    178             if (DBG) {
    179                 Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
    180             }
    181             return retval;
    182         } catch (RemoteException e) {
    183             Log.e(TAG, "Error in getEnabledSpellCheckers: " + e);
    184             return null;
    185         }
    186     }
    187 
    188     /**
    189      * @hide
    190      */
    191     public SpellCheckerInfo getCurrentSpellChecker() {
    192         try {
    193             // Passing null as a locale for ICS
    194             return sService.getCurrentSpellChecker(null);
    195         } catch (RemoteException e) {
    196             return null;
    197         }
    198     }
    199 
    200     /**
    201      * @hide
    202      */
    203     public void setCurrentSpellChecker(SpellCheckerInfo sci) {
    204         try {
    205             if (sci == null) {
    206                 throw new NullPointerException("SpellCheckerInfo is null.");
    207             }
    208             sService.setCurrentSpellChecker(null, sci.getId());
    209         } catch (RemoteException e) {
    210             Log.e(TAG, "Error in setCurrentSpellChecker: " + e);
    211         }
    212     }
    213 
    214     /**
    215      * @hide
    216      */
    217     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
    218             boolean allowImplicitlySelectedSubtype) {
    219         try {
    220             // Passing null as a locale until we support multiple enabled spell checker subtypes.
    221             return sService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
    222         } catch (RemoteException e) {
    223             Log.e(TAG, "Error in getCurrentSpellCheckerSubtype: " + e);
    224             return null;
    225         }
    226     }
    227 
    228     /**
    229      * @hide
    230      */
    231     public void setSpellCheckerSubtype(SpellCheckerSubtype subtype) {
    232         try {
    233             final int hashCode;
    234             if (subtype == null) {
    235                 hashCode = 0;
    236             } else {
    237                 hashCode = subtype.hashCode();
    238             }
    239             sService.setCurrentSpellCheckerSubtype(null, hashCode);
    240         } catch (RemoteException e) {
    241             Log.e(TAG, "Error in setSpellCheckerSubtype:" + e);
    242         }
    243     }
    244 
    245     /**
    246      * @hide
    247      */
    248     public void setSpellCheckerEnabled(boolean enabled) {
    249         try {
    250             sService.setSpellCheckerEnabled(enabled);
    251         } catch (RemoteException e) {
    252             Log.e(TAG, "Error in setSpellCheckerEnabled:" + e);
    253         }
    254     }
    255 
    256     /**
    257      * @hide
    258      */
    259     public boolean isSpellCheckerEnabled() {
    260         try {
    261             return sService.isSpellCheckerEnabled();
    262         } catch (RemoteException e) {
    263             Log.e(TAG, "Error in isSpellCheckerEnabled:" + e);
    264             return false;
    265         }
    266     }
    267 }
    268