TextServicesManagerService.java revision da317ef68603dc7649f98bda495267973825e7fa
1988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/* 2988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Copyright (C) 2011 The Android Open Source Project 3988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * 4988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Licensed under the Apache License, Version 2.0 (the "License"); 5988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * you may not use this file except in compliance with the License. 6988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * You may obtain a copy of the License at 7988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * 8988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * http://www.apache.org/licenses/LICENSE-2.0 9988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * 10988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Unless required by applicable law or agreed to in writing, software 11988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * distributed under the License is distributed on an "AS IS" BASIS, 12988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * See the License for the specific language governing permissions and 14988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * limitations under the License. 15988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */ 16988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 17988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpackage com.android.server; 18988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 19988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.content.PackageMonitor; 20988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerService; 21988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSession; 22988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSessionListener; 23988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesManager; 24988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesSessionListener; 25988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 26988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.ComponentName; 27988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Context; 28988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Intent; 29988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.ServiceConnection; 30988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.PackageManager; 31988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.ResolveInfo; 32988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.ServiceInfo; 33988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.IBinder; 34988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.RemoteException; 35988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.SystemClock; 36988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.provider.Settings; 37988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.text.TextUtils; 38988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.service.textservice.SpellCheckerService; 39988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.util.Log; 40988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.util.Slog; 41988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.SpellCheckerInfo; 42988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 43988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.ArrayList; 44988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.HashMap; 45988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.HashSet; 46988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.List; 47988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 48988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpublic class TextServicesManagerService extends ITextServicesManager.Stub { 49988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final String TAG = TextServicesManagerService.class.getSimpleName(); 50988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final boolean DBG = false; 51988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 52988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final Context mContext; 53988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private boolean mSystemReady; 54988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final TextServicesMonitor mMonitor; 55988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap = 56988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new HashMap<String, SpellCheckerInfo>(); 57988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<SpellCheckerInfo>(); 58988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = 59988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new HashMap<String, SpellCheckerBindGroup>(); 60988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 61988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void systemReady() { 62988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!mSystemReady) { 63988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSystemReady = true; 64988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 65988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 66988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 67988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public TextServicesManagerService(Context context) { 68988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSystemReady = false; 69988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mContext = context; 70988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mMonitor = new TextServicesMonitor(); 71988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mMonitor.register(context, true); 72988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized (mSpellCheckerMap) { 73988323c57bd25a58f05dfa492d9b9c8ab62c5153satok buildSpellCheckerMapLocked(context, mSpellCheckerList, mSpellCheckerMap); 74988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 75988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 76988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 77988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class TextServicesMonitor extends PackageMonitor { 78988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 79988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onSomePackagesChanged() { 80988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized (mSpellCheckerMap) { 81988323c57bd25a58f05dfa492d9b9c8ab62c5153satok buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap); 82988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: Update for each locale 83988323c57bd25a58f05dfa492d9b9c8ab62c5153satok SpellCheckerInfo sci = getCurrentSpellChecker(null); 84da317ef68603dc7649f98bda495267973825e7fasatok if (sci == null) return; 85988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final String packageName = sci.getPackageName(); 86988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int change = isPackageDisappearing(packageName); 87988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE) { 88988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // Package disappearing 89988323c57bd25a58f05dfa492d9b9c8ab62c5153satok setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName)); 90988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } else if (isPackageModified(packageName)) { 91988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // Package modified 92988323c57bd25a58f05dfa492d9b9c8ab62c5153satok setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName)); 93988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 94988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 95988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 96988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 97988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 98988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static void buildSpellCheckerMapLocked(Context context, 99988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map) { 100988323c57bd25a58f05dfa492d9b9c8ab62c5153satok list.clear(); 101988323c57bd25a58f05dfa492d9b9c8ab62c5153satok map.clear(); 102988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final PackageManager pm = context.getPackageManager(); 103988323c57bd25a58f05dfa492d9b9c8ab62c5153satok List<ResolveInfo> services = pm.queryIntentServices( 104988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA); 105988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int N = services.size(); 106988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < N; ++i) { 107988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ResolveInfo ri = services.get(i); 108988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ServiceInfo si = ri.serviceInfo; 109988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ComponentName compName = new ComponentName(si.packageName, si.name); 110988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) { 111988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.w(TAG, "Skipping text service " + compName 112988323c57bd25a58f05dfa492d9b9c8ab62c5153satok + ": it does not require the permission " 113988323c57bd25a58f05dfa492d9b9c8ab62c5153satok + android.Manifest.permission.BIND_TEXT_SERVICE); 114988323c57bd25a58f05dfa492d9b9c8ab62c5153satok continue; 115988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 116988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (DBG) Slog.d(TAG, "Add: " + compName); 117988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final SpellCheckerInfo sci = new SpellCheckerInfo(context, ri); 118988323c57bd25a58f05dfa492d9b9c8ab62c5153satok list.add(sci); 119988323c57bd25a58f05dfa492d9b9c8ab62c5153satok map.put(sci.getId(), sci); 120988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 121da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 122da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "buildSpellCheckerMapLocked: " + list.size() + "," + map.size()); 123da317ef68603dc7649f98bda495267973825e7fasatok } 124988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 125988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 126988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: find an appropriate spell checker for specified locale 127988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private SpellCheckerInfo findAvailSpellCheckerLocked(String locale, String prefPackage) { 128988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int spellCheckersCount = mSpellCheckerList.size(); 129988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (spellCheckersCount == 0) { 130988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.w(TAG, "no available spell checker services found"); 131988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return null; 132988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 133988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (prefPackage != null) { 134988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < spellCheckersCount; ++i) { 135988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final SpellCheckerInfo sci = mSpellCheckerList.get(i); 136988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (prefPackage.equals(sci.getPackageName())) { 137da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 138da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "findAvailSpellCheckerLocked: " + sci.getPackageName()); 139da317ef68603dc7649f98bda495267973825e7fasatok } 140988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return sci; 141988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 142988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 143988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 144988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (spellCheckersCount > 1) { 145988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.w(TAG, "more than one spell checker service found, picking first"); 146988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 147988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerList.get(0); 148988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 149988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 150988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: Save SpellCheckerService by supported languages. Currently only one spell 151988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // checker is saved. 152988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 153988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerInfo getCurrentSpellChecker(String locale) { 154988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized (mSpellCheckerMap) { 155da317ef68603dc7649f98bda495267973825e7fasatok String curSpellCheckerId = 156988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Settings.Secure.getString(mContext.getContentResolver(), 157988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Settings.Secure.SPELL_CHECKER_SERVICE); 158562ab585f9e413d9696ee250e5ec02f95889a157satok if (DBG) { 159562ab585f9e413d9696ee250e5ec02f95889a157satok Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId); 160562ab585f9e413d9696ee250e5ec02f95889a157satok } 161988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (TextUtils.isEmpty(curSpellCheckerId)) { 162da317ef68603dc7649f98bda495267973825e7fasatok final SpellCheckerInfo sci = findAvailSpellCheckerLocked(null, null); 163da317ef68603dc7649f98bda495267973825e7fasatok if (sci == null) return null; 164da317ef68603dc7649f98bda495267973825e7fasatok // Set the current spell checker if there is one or more spell checkers 165da317ef68603dc7649f98bda495267973825e7fasatok // available. In this case, "sci" is the first one in the available spell 166da317ef68603dc7649f98bda495267973825e7fasatok // checkers. 167da317ef68603dc7649f98bda495267973825e7fasatok setCurrentSpellChecker(sci); 168da317ef68603dc7649f98bda495267973825e7fasatok return sci; 169988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 170988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerMap.get(curSpellCheckerId); 171988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 172988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 173988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 174988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 175988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void getSpellCheckerService(SpellCheckerInfo info, String locale, 176988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) { 177988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!mSystemReady) { 178988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 179988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 180988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (info == null || tsListener == null) { 181988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.e(TAG, "getSpellCheckerService: Invalid input."); 182988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 183988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 184988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final String sciId = info.getId(); 185988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 186988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!mSpellCheckerMap.containsKey(sciId)) { 187988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 188988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 189988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mSpellCheckerBindGroups.containsKey(sciId)) { 190988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerBindGroups.get(sciId).addListener(tsListener, locale, scListener); 191988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 192988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 193988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final InternalServiceConnection connection = new InternalServiceConnection( 194988323c57bd25a58f05dfa492d9b9c8ab62c5153satok sciId, locale, scListener); 195988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE); 196988323c57bd25a58f05dfa492d9b9c8ab62c5153satok serviceIntent.setComponent(info.getComponent()); 197988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { 198988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.e(TAG, "Failed to get a spell checker service."); 199988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 200988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 201988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final SpellCheckerBindGroup group = new SpellCheckerBindGroup( 202988323c57bd25a58f05dfa492d9b9c8ab62c5153satok connection, tsListener, locale, scListener); 203988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerBindGroups.put(sciId, group); 204988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 205988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 206988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 207988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 208988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 209562ab585f9e413d9696ee250e5ec02f95889a157satok public SpellCheckerInfo[] getEnabledSpellCheckers() { 210da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 211da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "getEnabledSpellCheckers: " + mSpellCheckerList.size()); 212da317ef68603dc7649f98bda495267973825e7fasatok for (int i = 0; i < mSpellCheckerList.size(); ++i) { 213da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "EnabledSpellCheckers: " + mSpellCheckerList.get(i).getPackageName()); 214da317ef68603dc7649f98bda495267973825e7fasatok } 215da317ef68603dc7649f98bda495267973825e7fasatok } 216562ab585f9e413d9696ee250e5ec02f95889a157satok return mSpellCheckerList.toArray(new SpellCheckerInfo[mSpellCheckerList.size()]); 217562ab585f9e413d9696ee250e5ec02f95889a157satok } 218562ab585f9e413d9696ee250e5ec02f95889a157satok 219562ab585f9e413d9696ee250e5ec02f95889a157satok @Override 220988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void finishSpellCheckerService(ISpellCheckerSessionListener listener) { 221da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 222da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "FinishSpellCheckerService"); 223da317ef68603dc7649f98bda495267973825e7fasatok } 224988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 225988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) { 226988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (group == null) continue; 227988323c57bd25a58f05dfa492d9b9c8ab62c5153satok group.removeListener(listener); 228988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 229988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 230988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 231988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 232988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void setCurrentSpellChecker(SpellCheckerInfo sci) { 233562ab585f9e413d9696ee250e5ec02f95889a157satok if (DBG) { 234562ab585f9e413d9696ee250e5ec02f95889a157satok Slog.w(TAG, "setCurrentSpellChecker: " + sci.getId()); 235562ab585f9e413d9696ee250e5ec02f95889a157satok } 236988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (sci == null || mSpellCheckerMap.containsKey(sci.getId())) return; 237988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Settings.Secure.putString(mContext.getContentResolver(), 238988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Settings.Secure.SPELL_CHECKER_SERVICE, sci == null ? "" : sci.getId()); 239988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 240988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 241988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // SpellCheckerBindGroup contains active text service session listeners. 242988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from 243988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // mSpellCheckerBindGroups 244988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class SpellCheckerBindGroup { 245988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final InternalServiceConnection mInternalConnection; 246988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ArrayList<InternalDeathRecipient> mListeners = 247988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new ArrayList<InternalDeathRecipient>(); 248988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 249988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerBindGroup(InternalServiceConnection connection, 250988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ITextServicesSessionListener listener, String locale, 251988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ISpellCheckerSessionListener scListener) { 252988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mInternalConnection = connection; 253988323c57bd25a58f05dfa492d9b9c8ab62c5153satok addListener(listener, locale, scListener); 254988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 255988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 256988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceConnected(ISpellCheckerService spellChecker) { 257da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 258da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "onServiceConnected"); 259da317ef68603dc7649f98bda495267973825e7fasatok } 260988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 261988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (InternalDeathRecipient listener : mListeners) { 262988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 263988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ISpellCheckerSession session = spellChecker.getISpellCheckerSession( 264988323c57bd25a58f05dfa492d9b9c8ab62c5153satok listener.mScLocale, listener.mScListener); 265988323c57bd25a58f05dfa492d9b9c8ab62c5153satok listener.mTsListener.onServiceConnected(session); 266988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 267988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 268988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 269988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 270988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 271988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 272988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void addListener(ITextServicesSessionListener tsListener, String locale, 273988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ISpellCheckerSessionListener scListener) { 274da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 275da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "addListener: " + locale); 276da317ef68603dc7649f98bda495267973825e7fasatok } 277988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 278988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 279988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int size = mListeners.size(); 280988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < size; ++i) { 281988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mListeners.get(i).hasSpellCheckerListener(scListener)) { 282988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // do not add the lister if the group already contains this. 283988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 284988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 285988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 286988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final InternalDeathRecipient recipient = new InternalDeathRecipient( 287988323c57bd25a58f05dfa492d9b9c8ab62c5153satok this, tsListener, locale, scListener); 288988323c57bd25a58f05dfa492d9b9c8ab62c5153satok scListener.asBinder().linkToDeath(recipient, 0); 289988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mListeners.add(new InternalDeathRecipient( 290988323c57bd25a58f05dfa492d9b9c8ab62c5153satok this, tsListener, locale, scListener)); 291988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch(RemoteException e) { 292988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // do nothing 293988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 294988323c57bd25a58f05dfa492d9b9c8ab62c5153satok cleanLocked(); 295988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 296988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 297988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 298988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void removeListener(ISpellCheckerSessionListener listener) { 299da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 300da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "remove listener"); 301da317ef68603dc7649f98bda495267973825e7fasatok } 302988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 303988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int size = mListeners.size(); 304988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ArrayList<InternalDeathRecipient> removeList = 305988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new ArrayList<InternalDeathRecipient>(); 306988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < size; ++i) { 307988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final InternalDeathRecipient tempRecipient = mListeners.get(i); 308988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if(tempRecipient.hasSpellCheckerListener(listener)) { 309988323c57bd25a58f05dfa492d9b9c8ab62c5153satok removeList.add(tempRecipient); 310988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 311988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 312988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int removeSize = removeList.size(); 313988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < removeSize; ++i) { 314988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mListeners.remove(removeList.get(i)); 315988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 316988323c57bd25a58f05dfa492d9b9c8ab62c5153satok cleanLocked(); 317988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 318988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 319988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 320988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void cleanLocked() { 321da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 322da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "cleanLocked"); 323da317ef68603dc7649f98bda495267973825e7fasatok } 324988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mListeners.isEmpty()) { 325988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerBindGroups.remove(this); 326988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // Unbind service when there is no active clients. 327988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mContext.unbindService(mInternalConnection); 328988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 329988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 330988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 331988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 332988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class InternalServiceConnection implements ServiceConnection { 333988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final ISpellCheckerSessionListener mListener; 334988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final String mSciId; 335988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final String mLocale; 336988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public InternalServiceConnection( 337988323c57bd25a58f05dfa492d9b9c8ab62c5153satok String id, String locale, ISpellCheckerSessionListener listener) { 338988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSciId = id; 339988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mLocale = locale; 340988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mListener = listener; 341988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 342988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 343988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 344988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceConnected(ComponentName name, IBinder service) { 345988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 346988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service); 347988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); 348988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (group != null) { 349988323c57bd25a58f05dfa492d9b9c8ab62c5153satok group.onServiceConnected(spellChecker); 350988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 351988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 352988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 353988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 354988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 355988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceDisconnected(ComponentName name) { 356988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerBindGroups.remove(mSciId); 357988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 358988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 359988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 360988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class InternalDeathRecipient implements IBinder.DeathRecipient { 361988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final ITextServicesSessionListener mTsListener; 362988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final ISpellCheckerSessionListener mScListener; 363988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final String mScLocale; 364988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final SpellCheckerBindGroup mGroup; 365988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public InternalDeathRecipient(SpellCheckerBindGroup group, 366988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ITextServicesSessionListener tsListener, String scLocale, 367988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ISpellCheckerSessionListener scListener) { 368988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTsListener = tsListener; 369988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mScListener = scListener; 370988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mScLocale = scLocale; 371988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mGroup = group; 372988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 373988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 374988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) { 375988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mScListener.equals(listener); 376988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 377988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 378988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 379988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void binderDied() { 380988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mGroup.removeListener(mScListener); 381988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 382988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 383988323c57bd25a58f05dfa492d9b9c8ab62c5153satok} 384