blob: 2b8a4582896e728f119ca5e3d5a1da7050a9c6e3 [file] [log] [blame]
/*
* 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.service.textservice;
import com.android.internal.textservice.ISpellCheckerService;
import com.android.internal.textservice.ISpellCheckerSession;
import com.android.internal.textservice.ISpellCheckerSessionListener;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.view.textservice.SuggestionsInfo;
import android.view.textservice.TextInfo;
import java.lang.ref.WeakReference;
/**
* SpellCheckerService provides an abstract base class for a spell checker.
* This class combines a service to the system with the spell checker service interface that
* spell checker must implement.
*
* <p>In addition to the normal Service lifecycle methods, this class
* introduces a new specific callback that subclasses should override
* {@link #createSession()} to provide a spell checker session that is corresponding
* to requested language and so on. The spell checker session returned by this method
* should extend {@link SpellCheckerService.Session}.
* </p>
*
* <h3>Returning spell check results</h3>
*
* <p>{@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
* should return spell check results.
* It receives {@link android.view.textservice.TextInfo} and returns
* {@link android.view.textservice.SuggestionsInfo} for the input.
* You may want to override
* {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} for
* better performance and quality.
* </p>
*
* <p>Please note that {@link SpellCheckerService.Session#getLocale()} does not return a valid
* locale before {@link SpellCheckerService.Session#onCreate()} </p>
*
*/
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";
private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this);
/**
* Implement to return the implementation of the internal spell checker
* service interface. Subclasses should not override.
*/
@Override
public final IBinder onBind(final Intent intent) {
if (DBG) {
Log.w(TAG, "onBind");
}
return mBinder;
}
/**
* Factory method to create a spell checker session impl
* @return SpellCheckerSessionImpl which should be overridden by a concrete implementation.
*/
public abstract Session createSession();
/**
* This abstract class should be overridden by a concrete implementation of a spell checker.
*/
public static abstract class Session {
private InternalISpellCheckerSession mInternalSession;
/**
* @hide
*/
public final void setInternalISpellCheckerSession(InternalISpellCheckerSession session) {
mInternalSession = session;
}
/**
* This is called after the class is initialized, at which point it knows it can call
* getLocale() etc...
*/
public abstract void onCreate();
/**
* Get suggestions for specified text in TextInfo.
* This function will run on the incoming IPC thread.
* So, this is not called on the main thread,
* but will be called in series on another thread.
* @param textInfo the text metadata
* @param suggestionsLimit the number of limit of suggestions returned
* @return SuggestionsInfo which contains suggestions for textInfo
*/
public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit);
/**
* A batch process of onGetSuggestions.
* This function will run on the incoming IPC thread.
* So, this is not called on the main thread,
* but will be called in series on another thread.
* @param textInfos an array of the text metadata
* @param suggestionsLimit the number of limit of suggestions returned
* @param sequentialWords true if textInfos can be treated as sequential words.
* @return an array of SuggestionsInfo of onGetSuggestions
*/
public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
int suggestionsLimit, boolean sequentialWords) {
final int length = textInfos.length;
final SuggestionsInfo[] retval = new SuggestionsInfo[length];
for (int i = 0; i < length; ++i) {
retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
retval[i].setCookieAndSequence(
textInfos[i].getCookie(), textInfos[i].getSequence());
}
return retval;
}
/**
* @hide
* The default implementation returns an array of SuggestionsInfo by simply calling
* onGetSuggestions().
* When you override this method, make sure that suggestionsLimit is applied to suggestions
* that share the same start position and length.
*/
public SuggestionsInfo[] onGetSuggestionsMultipleForSentence(TextInfo[] textInfos,
int suggestionsLimit) {
final int length = textInfos.length;
final SuggestionsInfo[] retval = new SuggestionsInfo[length];
for (int i = 0; i < length; ++i) {
retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
retval[i].setCookieAndSequence(
textInfos[i].getCookie(), textInfos[i].getSequence());
}
return retval;
}
/**
* Request to abort all tasks executed in SpellChecker.
* This function will run on the incoming IPC thread.
* So, this is not called on the main thread,
* but will be called in series on another thread.
*/
public void onCancel() {}
/**
* Request to close this session.
* This function will run on the incoming IPC thread.
* So, this is not called on the main thread,
* but will be called in series on another thread.
*/
public void onClose() {}
/**
* @return Locale for this session
*/
public String getLocale() {
return mInternalSession.getLocale();
}
/**
* @return Bundle for this session
*/
public Bundle getBundle() {
return mInternalSession.getBundle();
}
}
// Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub {
private ISpellCheckerSessionListener mListener;
private final Session mSession;
private final String mLocale;
private final Bundle mBundle;
public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener,
Bundle bundle, Session session) {
mListener = listener;
mSession = session;
mLocale = locale;
mBundle = bundle;
session.setInternalISpellCheckerSession(this);
}
@Override
public void onGetSuggestionsMultiple(
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
int pri = Process.getThreadPriority(Process.myTid());
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mListener.onGetSuggestions(
mSession.onGetSuggestionsMultiple(
textInfos, suggestionsLimit, sequentialWords));
} catch (RemoteException e) {
} finally {
Process.setThreadPriority(pri);
}
}
@Override
public void onGetSuggestionsMultipleForSentence(
TextInfo[] textInfos, int suggestionsLimit) {
try {
mListener.onGetSuggestionsForSentence(
mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit));
} catch (RemoteException e) {
}
}
@Override
public void onCancel() {
int pri = Process.getThreadPriority(Process.myTid());
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mSession.onCancel();
} finally {
Process.setThreadPriority(pri);
}
}
@Override
public void onClose() {
int pri = Process.getThreadPriority(Process.myTid());
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mSession.onClose();
} finally {
Process.setThreadPriority(pri);
mListener = null;
}
}
public String getLocale() {
return mLocale;
}
public Bundle getBundle() {
return mBundle;
}
}
private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub {
private final WeakReference<SpellCheckerService> mInternalServiceRef;
public SpellCheckerServiceBinder(SpellCheckerService service) {
mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
}
@Override
public ISpellCheckerSession getISpellCheckerSession(
String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
final SpellCheckerService service = mInternalServiceRef.get();
if (service == null) return null;
final Session session = service.createSession();
final InternalISpellCheckerSession internalSession =
new InternalISpellCheckerSession(locale, listener, bundle, session);
session.onCreate();
return internalSession;
}
}
}