Loading core/java/android/service/textservice/SpellCheckerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; Loading @@ -36,6 +37,7 @@ import java.lang.ref.WeakReference; */ public abstract class SpellCheckerService extends Service { private static final String TAG = SpellCheckerService.class.getSimpleName(); private static final boolean DBG = false; public static final String SERVICE_INTERFACE = "android.service.textservice.SpellCheckerService"; Loading Loading @@ -87,6 +89,9 @@ public abstract class SpellCheckerService extends Service { */ @Override public final IBinder onBind(final Intent intent) { if (DBG) { Log.w(TAG, "onBind"); } return mBinder; } Loading core/java/android/service/textservice/SpellCheckerSession.java +18 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import java.util.LinkedList; import java.util.Locale; import java.util.Queue; /** Loading Loading @@ -125,6 +124,9 @@ public class SpellCheckerSession { */ public void getSuggestions( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { if (DBG) { Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); } // TODO: Handle multiple words suggestions by using WordBreakIterator mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( textInfos, suggestionsLimit, sequentialWords); Loading Loading @@ -186,6 +188,9 @@ public class SpellCheckerSession { public void getSuggestionsMultiple( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { if (DBG) { Log.w(TAG, "getSuggestionsMultiple"); } processOrEnqueueTask( new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos, suggestionsLimit, sequentialWords)); Loading @@ -204,6 +209,9 @@ public class SpellCheckerSession { } private void processOrEnqueueTask(SpellCheckerParams scp) { if (DBG) { Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession); } if (mISpellCheckerSession == null) { mPendingTasks.offer(scp); } else { Loading @@ -215,6 +223,9 @@ public class SpellCheckerSession { if (!checkOpenConnection()) { return; } if (DBG) { Log.w(TAG, "Cancel spell checker tasks."); } try { mISpellCheckerSession.cancel(); } catch (RemoteException e) { Loading @@ -226,6 +237,9 @@ public class SpellCheckerSession { if (!checkOpenConnection()) { return; } if (DBG) { Log.w(TAG, "Get suggestions from the spell checker."); } try { mISpellCheckerSession.getSuggestionsMultiple( scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords); Loading Loading @@ -254,6 +268,9 @@ public class SpellCheckerSession { private class InternalListener extends ITextServicesSessionListener.Stub { @Override public void onServiceConnected(ISpellCheckerSession session) { if (DBG) { Log.w(TAG, "SpellCheckerSession connected."); } mSpellCheckerSessionListenerImpl.onServiceConnected(session); } } Loading services/java/com/android/server/TextServicesManagerService.java +84 −20 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; Loading Loading @@ -177,7 +178,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (!mSystemReady) { return; } if (info == null || tsListener == null) { if (info == null || tsListener == null || scListener == null) { Slog.e(TAG, "getSpellCheckerService: Invalid input."); return; } Loading @@ -187,13 +188,57 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { return; } if (mSpellCheckerBindGroups.containsKey(sciId)) { mSpellCheckerBindGroups.get(sciId).addListener(tsListener, locale, scListener); final SpellCheckerBindGroup bindGroup = mSpellCheckerBindGroups.get(sciId); if (bindGroup != null) { final InternalDeathRecipient recipient = mSpellCheckerBindGroups.get(sciId).addListener( tsListener, locale, scListener); if (recipient == null) { if (DBG) { Slog.w(TAG, "Didn't create a death recipient."); } return; } if (bindGroup.mSpellChecker == null & bindGroup.mConnected) { Slog.e(TAG, "The state of the spell checker bind group is illegal."); bindGroup.removeAll(); } else if (bindGroup.mSpellChecker != null) { if (DBG) { Slog.w(TAG, "Existing bind found. Return a spell checker session now."); } try { final ISpellCheckerSession session = bindGroup.mSpellChecker.getISpellCheckerSession( recipient.mScLocale, recipient.mScListener); tsListener.onServiceConnected(session); return; } catch (RemoteException e) { Slog.e(TAG, "Exception in getting spell checker session: " + e); bindGroup.removeAll(); } } } } final long ident = Binder.clearCallingIdentity(); try { startSpellCheckerServiceInnerLocked(info, locale, tsListener, scListener); } finally { Binder.restoreCallingIdentity(ident); } } return; } private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale, ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) { final String sciId = info.getId(); final InternalServiceConnection connection = new InternalServiceConnection( sciId, locale, scListener); final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE); serviceIntent.setComponent(info.getComponent()); if (DBG) { Slog.w(TAG, "bind service: " + info.getId()); } if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { Slog.e(TAG, "Failed to get a spell checker service."); return; Loading @@ -202,8 +247,6 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { connection, tsListener, locale, scListener); mSpellCheckerBindGroups.put(sciId, group); } return; } @Override public SpellCheckerInfo[] getEnabledSpellCheckers() { Loading Loading @@ -242,14 +285,17 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from // mSpellCheckerBindGroups private class SpellCheckerBindGroup { final InternalServiceConnection mInternalConnection; final ArrayList<InternalDeathRecipient> mListeners = private final InternalServiceConnection mInternalConnection; private final ArrayList<InternalDeathRecipient> mListeners = new ArrayList<InternalDeathRecipient>(); public ISpellCheckerService mSpellChecker; public boolean mConnected; public SpellCheckerBindGroup(InternalServiceConnection connection, ITextServicesSessionListener listener, String locale, ISpellCheckerSessionListener scListener) { mInternalConnection = connection; mConnected = false; addListener(listener, locale, scListener); } Loading @@ -264,26 +310,32 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { listener.mScLocale, listener.mScListener); listener.mTsListener.onServiceConnected(session); } catch (RemoteException e) { Slog.e(TAG, "Exception in getting spell checker session: " + e); removeAll(); return; } } mSpellChecker = spellChecker; mConnected = true; } } public void addListener(ITextServicesSessionListener tsListener, String locale, ISpellCheckerSessionListener scListener) { public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener, String locale, ISpellCheckerSessionListener scListener) { if (DBG) { Slog.d(TAG, "addListener: " + locale); } InternalDeathRecipient recipient = null; synchronized(mSpellCheckerMap) { try { final int size = mListeners.size(); for (int i = 0; i < size; ++i) { if (mListeners.get(i).hasSpellCheckerListener(scListener)) { // do not add the lister if the group already contains this. return; return null; } } final InternalDeathRecipient recipient = new InternalDeathRecipient( recipient = new InternalDeathRecipient( this, tsListener, locale, scListener); scListener.asBinder().linkToDeath(recipient, 0); mListeners.add(new InternalDeathRecipient( Loading @@ -293,6 +345,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } cleanLocked(); } return recipient; } public void removeListener(ISpellCheckerSessionListener listener) { Loading Loading @@ -322,11 +375,19 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Slog.d(TAG, "cleanLocked"); } if (mListeners.isEmpty()) { if (mSpellCheckerBindGroups.containsKey(this)) { mSpellCheckerBindGroups.remove(this); } // Unbind service when there is no active clients. mContext.unbindService(mInternalConnection); } } public void removeAll() { Slog.e(TAG, "Remove the spell checker bind unexpectedly."); mListeners.clear(); cleanLocked(); } } private class InternalServiceConnection implements ServiceConnection { Loading @@ -343,6 +404,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized(mSpellCheckerMap) { if (DBG) { Slog.w(TAG, "onServiceConnected: " + name); } ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service); final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); if (group != null) { Loading Loading
core/java/android/service/textservice/SpellCheckerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; Loading @@ -36,6 +37,7 @@ import java.lang.ref.WeakReference; */ public abstract class SpellCheckerService extends Service { private static final String TAG = SpellCheckerService.class.getSimpleName(); private static final boolean DBG = false; public static final String SERVICE_INTERFACE = "android.service.textservice.SpellCheckerService"; Loading Loading @@ -87,6 +89,9 @@ public abstract class SpellCheckerService extends Service { */ @Override public final IBinder onBind(final Intent intent) { if (DBG) { Log.w(TAG, "onBind"); } return mBinder; } Loading
core/java/android/service/textservice/SpellCheckerSession.java +18 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import java.util.LinkedList; import java.util.Locale; import java.util.Queue; /** Loading Loading @@ -125,6 +124,9 @@ public class SpellCheckerSession { */ public void getSuggestions( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { if (DBG) { Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); } // TODO: Handle multiple words suggestions by using WordBreakIterator mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( textInfos, suggestionsLimit, sequentialWords); Loading Loading @@ -186,6 +188,9 @@ public class SpellCheckerSession { public void getSuggestionsMultiple( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { if (DBG) { Log.w(TAG, "getSuggestionsMultiple"); } processOrEnqueueTask( new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos, suggestionsLimit, sequentialWords)); Loading @@ -204,6 +209,9 @@ public class SpellCheckerSession { } private void processOrEnqueueTask(SpellCheckerParams scp) { if (DBG) { Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession); } if (mISpellCheckerSession == null) { mPendingTasks.offer(scp); } else { Loading @@ -215,6 +223,9 @@ public class SpellCheckerSession { if (!checkOpenConnection()) { return; } if (DBG) { Log.w(TAG, "Cancel spell checker tasks."); } try { mISpellCheckerSession.cancel(); } catch (RemoteException e) { Loading @@ -226,6 +237,9 @@ public class SpellCheckerSession { if (!checkOpenConnection()) { return; } if (DBG) { Log.w(TAG, "Get suggestions from the spell checker."); } try { mISpellCheckerSession.getSuggestionsMultiple( scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords); Loading Loading @@ -254,6 +268,9 @@ public class SpellCheckerSession { private class InternalListener extends ITextServicesSessionListener.Stub { @Override public void onServiceConnected(ISpellCheckerSession session) { if (DBG) { Log.w(TAG, "SpellCheckerSession connected."); } mSpellCheckerSessionListenerImpl.onServiceConnected(session); } } Loading
services/java/com/android/server/TextServicesManagerService.java +84 −20 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; Loading Loading @@ -177,7 +178,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (!mSystemReady) { return; } if (info == null || tsListener == null) { if (info == null || tsListener == null || scListener == null) { Slog.e(TAG, "getSpellCheckerService: Invalid input."); return; } Loading @@ -187,13 +188,57 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { return; } if (mSpellCheckerBindGroups.containsKey(sciId)) { mSpellCheckerBindGroups.get(sciId).addListener(tsListener, locale, scListener); final SpellCheckerBindGroup bindGroup = mSpellCheckerBindGroups.get(sciId); if (bindGroup != null) { final InternalDeathRecipient recipient = mSpellCheckerBindGroups.get(sciId).addListener( tsListener, locale, scListener); if (recipient == null) { if (DBG) { Slog.w(TAG, "Didn't create a death recipient."); } return; } if (bindGroup.mSpellChecker == null & bindGroup.mConnected) { Slog.e(TAG, "The state of the spell checker bind group is illegal."); bindGroup.removeAll(); } else if (bindGroup.mSpellChecker != null) { if (DBG) { Slog.w(TAG, "Existing bind found. Return a spell checker session now."); } try { final ISpellCheckerSession session = bindGroup.mSpellChecker.getISpellCheckerSession( recipient.mScLocale, recipient.mScListener); tsListener.onServiceConnected(session); return; } catch (RemoteException e) { Slog.e(TAG, "Exception in getting spell checker session: " + e); bindGroup.removeAll(); } } } } final long ident = Binder.clearCallingIdentity(); try { startSpellCheckerServiceInnerLocked(info, locale, tsListener, scListener); } finally { Binder.restoreCallingIdentity(ident); } } return; } private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale, ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) { final String sciId = info.getId(); final InternalServiceConnection connection = new InternalServiceConnection( sciId, locale, scListener); final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE); serviceIntent.setComponent(info.getComponent()); if (DBG) { Slog.w(TAG, "bind service: " + info.getId()); } if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { Slog.e(TAG, "Failed to get a spell checker service."); return; Loading @@ -202,8 +247,6 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { connection, tsListener, locale, scListener); mSpellCheckerBindGroups.put(sciId, group); } return; } @Override public SpellCheckerInfo[] getEnabledSpellCheckers() { Loading Loading @@ -242,14 +285,17 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from // mSpellCheckerBindGroups private class SpellCheckerBindGroup { final InternalServiceConnection mInternalConnection; final ArrayList<InternalDeathRecipient> mListeners = private final InternalServiceConnection mInternalConnection; private final ArrayList<InternalDeathRecipient> mListeners = new ArrayList<InternalDeathRecipient>(); public ISpellCheckerService mSpellChecker; public boolean mConnected; public SpellCheckerBindGroup(InternalServiceConnection connection, ITextServicesSessionListener listener, String locale, ISpellCheckerSessionListener scListener) { mInternalConnection = connection; mConnected = false; addListener(listener, locale, scListener); } Loading @@ -264,26 +310,32 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { listener.mScLocale, listener.mScListener); listener.mTsListener.onServiceConnected(session); } catch (RemoteException e) { Slog.e(TAG, "Exception in getting spell checker session: " + e); removeAll(); return; } } mSpellChecker = spellChecker; mConnected = true; } } public void addListener(ITextServicesSessionListener tsListener, String locale, ISpellCheckerSessionListener scListener) { public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener, String locale, ISpellCheckerSessionListener scListener) { if (DBG) { Slog.d(TAG, "addListener: " + locale); } InternalDeathRecipient recipient = null; synchronized(mSpellCheckerMap) { try { final int size = mListeners.size(); for (int i = 0; i < size; ++i) { if (mListeners.get(i).hasSpellCheckerListener(scListener)) { // do not add the lister if the group already contains this. return; return null; } } final InternalDeathRecipient recipient = new InternalDeathRecipient( recipient = new InternalDeathRecipient( this, tsListener, locale, scListener); scListener.asBinder().linkToDeath(recipient, 0); mListeners.add(new InternalDeathRecipient( Loading @@ -293,6 +345,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } cleanLocked(); } return recipient; } public void removeListener(ISpellCheckerSessionListener listener) { Loading Loading @@ -322,11 +375,19 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Slog.d(TAG, "cleanLocked"); } if (mListeners.isEmpty()) { if (mSpellCheckerBindGroups.containsKey(this)) { mSpellCheckerBindGroups.remove(this); } // Unbind service when there is no active clients. mContext.unbindService(mInternalConnection); } } public void removeAll() { Slog.e(TAG, "Remove the spell checker bind unexpectedly."); mListeners.clear(); cleanLocked(); } } private class InternalServiceConnection implements ServiceConnection { Loading @@ -343,6 +404,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized(mSpellCheckerMap) { if (DBG) { Slog.w(TAG, "onServiceConnected: " + name); } ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service); final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); if (group != null) { Loading