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