| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package android.view.textservice; |
| |
| import android.annotation.NonNull; |
| import android.annotation.SystemService; |
| import android.annotation.UnsupportedAppUsage; |
| import android.annotation.UserIdInt; |
| import android.content.Context; |
| import android.os.Bundle; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.ServiceManager.ServiceNotFoundException; |
| import android.os.UserHandle; |
| import android.util.Log; |
| import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; |
| |
| import com.android.internal.textservice.ISpellCheckerSessionListener; |
| import com.android.internal.textservice.ITextServicesManager; |
| |
| import java.util.Locale; |
| |
| /** |
| * System API to the overall text services, which arbitrates interaction between applications |
| * and text services. |
| * |
| * The user can change the current text services in Settings. And also applications can specify |
| * the target text services. |
| * |
| * <h3>Architecture Overview</h3> |
| * |
| * <p>There are three primary parties involved in the text services |
| * framework (TSF) architecture:</p> |
| * |
| * <ul> |
| * <li> The <strong>text services manager</strong> as expressed by this class |
| * is the central point of the system that manages interaction between all |
| * other parts. It is expressed as the client-side API here which exists |
| * in each application context and communicates with a global system service |
| * that manages the interaction across all processes. |
| * <li> A <strong>text service</strong> implements a particular |
| * interaction model allowing the client application to retrieve information of text. |
| * The system binds to the current text service that is in use, causing it to be created and run. |
| * <li> Multiple <strong>client applications</strong> arbitrate with the text service |
| * manager for connections to text services. |
| * </ul> |
| * |
| * <h3>Text services sessions</h3> |
| * <ul> |
| * <li>The <strong>spell checker session</strong> is one of the text services. |
| * {@link android.view.textservice.SpellCheckerSession}</li> |
| * </ul> |
| * |
| */ |
| @SystemService(Context.TEXT_SERVICES_MANAGER_SERVICE) |
| public final class TextServicesManager { |
| private static final String TAG = TextServicesManager.class.getSimpleName(); |
| private static final boolean DBG = false; |
| |
| /** |
| * @deprecated Do not use. Just kept because of {@link UnsupportedAppUsage} in |
| * {@link #getInstance()}. |
| */ |
| @Deprecated |
| private static TextServicesManager sInstance; |
| |
| private final ITextServicesManager mService; |
| |
| @UserIdInt |
| private final int mUserId; |
| |
| private TextServicesManager(@UserIdInt int userId) throws ServiceNotFoundException { |
| mService = ITextServicesManager.Stub.asInterface( |
| ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE)); |
| mUserId = userId; |
| } |
| |
| /** |
| * The factory method of {@link TextServicesManager}. |
| * |
| * @param context {@link Context} from which {@link TextServicesManager} should be instantiated. |
| * @return {@link TextServicesManager} that is associated with {@link Context#getUserId()}. |
| * @throws ServiceNotFoundException When {@link TextServicesManager} is not available. |
| * @hide |
| */ |
| @NonNull |
| public static TextServicesManager createInstance(@NonNull Context context) |
| throws ServiceNotFoundException { |
| return new TextServicesManager(context.getUserId()); |
| } |
| |
| /** |
| * @deprecated Do not use. Just kept because of {@link UnsupportedAppUsage} in |
| * {@link #getInstance()}. |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public static TextServicesManager getInstance() { |
| synchronized (TextServicesManager.class) { |
| if (sInstance == null) { |
| try { |
| sInstance = new TextServicesManager(UserHandle.myUserId()); |
| } catch (ServiceNotFoundException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| return sInstance; |
| } |
| } |
| |
| /** |
| * Returns the language component of a given locale string. |
| */ |
| private static String parseLanguageFromLocaleString(String locale) { |
| final int idx = locale.indexOf('_'); |
| if (idx < 0) { |
| return locale; |
| } else { |
| return locale.substring(0, idx); |
| } |
| } |
| |
| /** |
| * Get a spell checker session for the specified spell checker |
| * @param locale the locale for the spell checker. If {@code locale} is null and |
| * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be |
| * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true, |
| * the locale specified in Settings will be returned only when it is same as {@code locale}. |
| * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is |
| * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be |
| * selected. |
| * @param listener a spell checker session lister for getting results from a spell checker. |
| * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled |
| * languages in settings will be returned. |
| * @return the spell checker session of the spell checker |
| */ |
| public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale, |
| SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) { |
| if (listener == null) { |
| throw new NullPointerException(); |
| } |
| if (!referToSpellCheckerLanguageSettings && locale == null) { |
| throw new IllegalArgumentException("Locale should not be null if you don't refer" |
| + " settings."); |
| } |
| |
| if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) { |
| return null; |
| } |
| |
| final SpellCheckerInfo sci; |
| try { |
| sci = mService.getCurrentSpellChecker(mUserId, null); |
| } catch (RemoteException e) { |
| return null; |
| } |
| if (sci == null) { |
| return null; |
| } |
| SpellCheckerSubtype subtypeInUse = null; |
| if (referToSpellCheckerLanguageSettings) { |
| subtypeInUse = getCurrentSpellCheckerSubtype(true); |
| if (subtypeInUse == null) { |
| return null; |
| } |
| if (locale != null) { |
| final String subtypeLocale = subtypeInUse.getLocale(); |
| final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale); |
| if (subtypeLanguage.length() < 2 || !locale.getLanguage().equals(subtypeLanguage)) { |
| return null; |
| } |
| } |
| } else { |
| final String localeStr = locale.toString(); |
| for (int i = 0; i < sci.getSubtypeCount(); ++i) { |
| final SpellCheckerSubtype subtype = sci.getSubtypeAt(i); |
| final String tempSubtypeLocale = subtype.getLocale(); |
| final String tempSubtypeLanguage = parseLanguageFromLocaleString(tempSubtypeLocale); |
| if (tempSubtypeLocale.equals(localeStr)) { |
| subtypeInUse = subtype; |
| break; |
| } else if (tempSubtypeLanguage.length() >= 2 && |
| locale.getLanguage().equals(tempSubtypeLanguage)) { |
| subtypeInUse = subtype; |
| } |
| } |
| } |
| if (subtypeInUse == null) { |
| return null; |
| } |
| final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener); |
| try { |
| mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(), |
| session.getTextServicesSessionListener(), |
| session.getSpellCheckerSessionListener(), bundle); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| return session; |
| } |
| |
| /** |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public SpellCheckerInfo[] getEnabledSpellCheckers() { |
| try { |
| final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers(mUserId); |
| if (DBG) { |
| Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null")); |
| } |
| return retval; |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public SpellCheckerInfo getCurrentSpellChecker() { |
| try { |
| // Passing null as a locale for ICS |
| return mService.getCurrentSpellChecker(mUserId, null); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public SpellCheckerSubtype getCurrentSpellCheckerSubtype( |
| boolean allowImplicitlySelectedSubtype) { |
| try { |
| return mService.getCurrentSpellCheckerSubtype(mUserId, allowImplicitlySelectedSubtype); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public boolean isSpellCheckerEnabled() { |
| try { |
| return mService.isSpellCheckerEnabled(mUserId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| void finishSpellCheckerService(ISpellCheckerSessionListener listener) { |
| try { |
| mService.finishSpellCheckerService(mUserId, listener); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| } |