summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt8
-rw-r--r--core/java/android/service/textservice/SpellCheckerService.java185
-rw-r--r--core/java/android/view/textservice/SpellCheckerInfo.java13
-rw-r--r--core/java/android/view/textservice/SpellCheckerSession.java16
-rw-r--r--core/java/android/widget/SpellChecker.java2
-rwxr-xr-xcore/res/res/values/attrs.xml2
-rw-r--r--core/res/res/values/public.xml2
7 files changed, 184 insertions, 44 deletions
diff --git a/api/current.txt b/api/current.txt
index 2f8dcb1..2403fff 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -534,7 +534,7 @@ package android {
field public static final int imeSubtypeLocale = 16843500; // 0x10102ec
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
- field public static final int importantForAccessibility = 16843699; // 0x10103b3
+ field public static final int importantForAccessibility = 16843698; // 0x10103b2
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -936,7 +936,6 @@ package android {
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsRtl = 16843688; // 0x10103a8
- field public static final int supportsSentenceSpellCheck = 16843698; // 0x10103b2
field public static final int supportsUploading = 16843419; // 0x101029b
field public static final int switchMinWidth = 16843632; // 0x1010370
field public static final int switchPadding = 16843633; // 0x1010371
@@ -25425,9 +25424,8 @@ package android.view.textservice {
method public void close();
method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int);
method public android.view.textservice.SpellCheckerInfo getSpellChecker();
- method public void getSuggestions(android.view.textservice.TextInfo, int);
- method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
- method public boolean isSentenceSpellCheckSupported();
+ method public deprecated void getSuggestions(android.view.textservice.TextInfo, int);
+ method public deprecated void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
method public boolean isSessionDisconnected();
field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs";
}
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 53ce32d..c579e6e 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -26,12 +26,18 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
+import android.text.TextUtils;
+import android.text.method.WordIterator;
import android.util.Log;
import android.view.textservice.SentenceSuggestionsInfo;
import android.view.textservice.SuggestionsInfo;
import android.view.textservice.TextInfo;
+import android.widget.SpellChecker;
import java.lang.ref.WeakReference;
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.Locale;
/**
* SpellCheckerService provides an abstract base class for a spell checker.
@@ -92,6 +98,7 @@ public abstract class SpellCheckerService extends Service {
*/
public static abstract class Session {
private InternalISpellCheckerSession mInternalSession;
+ private volatile SentenceLevelAdapter mSentenceLevelAdapter;
/**
* @hide
@@ -142,8 +149,8 @@ public abstract class SpellCheckerService extends Service {
/**
* Get sentence suggestions for specified texts in an array of TextInfo.
- * The default implementation returns an array of SentenceSuggestionsInfo by simply
- * calling onGetSuggestions.
+ * The default implementation splits the input text to words and returns
+ * {@link SentenceSuggestionsInfo} which contains suggestions for each word.
* 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.
@@ -156,14 +163,41 @@ public abstract class SpellCheckerService extends Service {
*/
public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
int suggestionsLimit) {
- final int length = textInfos.length;
- final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length];
- for (int i = 0; i < length; ++i) {
- final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit);
- si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence());
- final int N = textInfos[i].getText().length();
- retval[i] = new SentenceSuggestionsInfo(
- new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N});
+ if (textInfos == null || textInfos.length == 0) {
+ return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+ }
+ if (DBG) {
+ Log.d(TAG, "onGetSentenceSuggestionsMultiple: + " + textInfos.length + ", "
+ + suggestionsLimit);
+ }
+ if (mSentenceLevelAdapter == null) {
+ synchronized(this) {
+ if (mSentenceLevelAdapter == null) {
+ final String localeStr = getLocale();
+ if (!TextUtils.isEmpty(localeStr)) {
+ mSentenceLevelAdapter = new SentenceLevelAdapter(new Locale(localeStr));
+ }
+ }
+ }
+ }
+ if (mSentenceLevelAdapter == null) {
+ return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+ }
+ final int infosSize = textInfos.length;
+ final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize];
+ for (int i = 0; i < infosSize; ++i) {
+ final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams =
+ mSentenceLevelAdapter.getSplitWords(textInfos[i]);
+ final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems =
+ textInfoParams.mItems;
+ final int itemsSize = mItems.size();
+ final TextInfo[] splitTextInfos = new TextInfo[itemsSize];
+ for (int j = 0; j < itemsSize; ++j) {
+ splitTextInfos[j] = mItems.get(j).mTextInfo;
+ }
+ retval[i] = SentenceLevelAdapter.reconstructSuggestions(
+ textInfoParams, onGetSuggestionsMultiple(
+ splitTextInfos, suggestionsLimit, true));
}
return retval;
}
@@ -290,4 +324,135 @@ public abstract class SpellCheckerService extends Service {
return internalSession;
}
}
+
+ /**
+ * Adapter class to accommodate word level spell checking APIs to sentence level spell checking
+ * APIs used in
+ * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)}
+ */
+ private static class SentenceLevelAdapter {
+ public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS =
+ new SentenceSuggestionsInfo[] {};
+ private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null);
+ /**
+ * Container for split TextInfo parameters
+ */
+ public static class SentenceWordItem {
+ public final TextInfo mTextInfo;
+ public final int mStart;
+ public final int mLength;
+ public SentenceWordItem(TextInfo ti, int start, int end) {
+ mTextInfo = ti;
+ mStart = start;
+ mLength = end - start;
+ }
+ }
+
+ /**
+ * Container for originally queried TextInfo and parameters
+ */
+ public static class SentenceTextInfoParams {
+ final TextInfo mOriginalTextInfo;
+ final ArrayList<SentenceWordItem> mItems;
+ final int mSize;
+ public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) {
+ mOriginalTextInfo = ti;
+ mItems = items;
+ mSize = items.size();
+ }
+ }
+
+ private final WordIterator mWordIterator;
+ public SentenceLevelAdapter(Locale locale) {
+ mWordIterator = new WordIterator(locale);
+ }
+
+ private SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) {
+ final WordIterator wordIterator = mWordIterator;
+ final CharSequence originalText = originalTextInfo.getText();
+ final int cookie = originalTextInfo.getCookie();
+ final int start = 0;
+ final int end = originalText.length();
+ final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>();
+ wordIterator.setCharSequence(originalText, 0, originalText.length());
+ int wordEnd = wordIterator.following(start);
+ int wordStart = wordIterator.getBeginning(wordEnd);
+ if (DBG) {
+ Log.d(TAG, "iterator: break: ---- 1st word start = " + wordStart + ", end = "
+ + wordEnd + "\n" + originalText);
+ }
+ while (wordStart <= end && wordEnd != BreakIterator.DONE
+ && wordStart != BreakIterator.DONE) {
+ if (wordEnd >= start && wordEnd > wordStart) {
+ final String query = originalText.subSequence(wordStart, wordEnd).toString();
+ final TextInfo ti = new TextInfo(query, cookie, query.hashCode());
+ wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
+ if (DBG) {
+ Log.d(TAG, "Adapter: word (" + (wordItems.size() - 1) + ") " + query);
+ }
+ }
+ wordEnd = wordIterator.following(wordEnd);
+ if (wordEnd == BreakIterator.DONE) {
+ break;
+ }
+ wordStart = wordIterator.getBeginning(wordEnd);
+ }
+ if (originalText.length() >= SpellChecker.WORD_ITERATOR_INTERVAL
+ && wordItems.size() >= 2) {
+ if (DBG) {
+ Log.w(TAG, "Remove possibly divided word: "
+ + wordItems.get(0).mTextInfo.getText());
+ }
+ wordItems.remove(0);
+ }
+ return new SentenceTextInfoParams(originalTextInfo, wordItems);
+ }
+
+ public static SentenceSuggestionsInfo reconstructSuggestions(
+ SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) {
+ if (results == null || results.length == 0) {
+ return null;
+ }
+ if (DBG) {
+ Log.w(TAG, "Adapter: onGetSuggestions: got " + results.length);
+ }
+ if (originalTextInfoParams == null) {
+ if (DBG) {
+ Log.w(TAG, "Adapter: originalTextInfoParams is null.");
+ }
+ return null;
+ }
+ final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie();
+ final int originalSequence =
+ originalTextInfoParams.mOriginalTextInfo.getSequence();
+
+ final int querySize = originalTextInfoParams.mSize;
+ final int[] offsets = new int[querySize];
+ final int[] lengths = new int[querySize];
+ final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize];
+ for (int i = 0; i < querySize; ++i) {
+ final SentenceWordItem item = originalTextInfoParams.mItems.get(i);
+ SuggestionsInfo result = null;
+ for (int j = 0; j < results.length; ++j) {
+ final SuggestionsInfo cur = results[j];
+ if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) {
+ result = cur;
+ result.setCookieAndSequence(originalCookie, originalSequence);
+ break;
+ }
+ }
+ offsets[i] = item.mStart;
+ lengths[i] = item.mLength;
+ reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO;
+ if (DBG) {
+ final int size = reconstructedSuggestions[i].getSuggestionsCount();
+ Log.w(TAG, "reconstructedSuggestions(" + i + ")" + size + ", first = "
+ + (size > 0 ? reconstructedSuggestions[i].getSuggestionAt(0)
+ : "<none>") + ", offset = " + offsets[i] + ", length = "
+ + lengths[i]);
+ }
+ }
+ return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths);
+ }
+ }
}
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index d05c1af..2167862 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -45,7 +45,6 @@ public final class SpellCheckerInfo implements Parcelable {
private final ResolveInfo mService;
private final String mId;
private final int mLabel;
- private final boolean mSupportsSentenceSpellCheck;
/**
* The spell checker setting activity's name, used by the system settings to
@@ -98,9 +97,6 @@ public final class SpellCheckerInfo implements Parcelable {
label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0);
settingsActivityComponent = sa.getString(
com.android.internal.R.styleable.SpellChecker_settingsActivity);
- mSupportsSentenceSpellCheck = sa.getBoolean(
- com.android.internal.R.styleable.SpellChecker_supportsSentenceSpellCheck,
- false);
sa.recycle();
final int depth = parser.getDepth();
@@ -142,7 +138,6 @@ public final class SpellCheckerInfo implements Parcelable {
*/
public SpellCheckerInfo(Parcel source) {
mLabel = source.readInt();
- mSupportsSentenceSpellCheck = source.readInt() != 0;
mId = source.readString();
mSettingsActivityName = source.readString();
mService = ResolveInfo.CREATOR.createFromParcel(source);
@@ -158,13 +153,6 @@ public final class SpellCheckerInfo implements Parcelable {
}
/**
- * @hide
- */
- public boolean isSentenceSpellCheckSupported() {
- return mSupportsSentenceSpellCheck;
- }
-
- /**
* Return the component of the service that implements.
*/
public ComponentName getComponent() {
@@ -188,7 +176,6 @@ public final class SpellCheckerInfo implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mLabel);
- dest.writeInt(mSupportsSentenceSpellCheck ? 1 : 0);
dest.writeString(mId);
dest.writeString(mSettingsActivityName);
mService.writeToParcel(dest, flags);
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 9dc05e4..628da3c 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -91,8 +91,6 @@ public class SpellCheckerSession {
* This meta-data must reference an XML resource.
**/
public static final String SERVICE_META_DATA = "android.view.textservice.scs";
- private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck";
-
private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
@@ -191,7 +189,9 @@ public class SpellCheckerSession {
* Get candidate strings for a substring of the specified text.
* @param textInfo text metadata for a spell checker
* @param suggestionsLimit the maximum number of suggestions that will be returned
+ * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
*/
+ @Deprecated
public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false);
}
@@ -201,13 +201,14 @@ public class SpellCheckerSession {
* @param textInfos an array of text metadata for a spell checker
* @param suggestionsLimit the maximum number of suggestions that will be returned
* @param sequentialWords true if textInfos can be treated as sequential words.
+ * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
*/
+ @Deprecated
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);
}
@@ -281,7 +282,7 @@ public class SpellCheckerSession {
break;
case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
if (DBG) {
- Log.w(TAG, "Get suggestions from the spell checker.");
+ Log.w(TAG, "Get sentence suggestions from the spell checker.");
}
try {
session.onGetSentenceSuggestionsMultiple(
@@ -492,11 +493,4 @@ public class SpellCheckerSession {
public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
return mSpellCheckerSessionListenerImpl;
}
-
- /**
- * @return true if the spell checker supports sentence level spell checking APIs
- */
- public boolean isSentenceSpellCheckSupported() {
- return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK);
- }
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index c725b64..e188207 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -116,7 +116,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
null /* Bundle not currently used by the textServicesManager */,
mCurrentLocale, this,
false /* means any available languages from current spell checker */);
- mIsSentenceSpellCheckSupported = mSpellCheckerSession.isSentenceSpellCheckSupported();
+ mIsSentenceSpellCheckSupported = true;
}
// Restore SpellCheckSpans in pool
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index de24d10..600a834 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2367,8 +2367,6 @@
<!-- Component name of an activity that allows the user to modify
the settings for this service. -->
<attr name="settingsActivity"/>
- <!-- Set true when the spell checker supports sentence level spell checking. -->
- <attr name="supportsSentenceSpellCheck" format="boolean" />
</declare-styleable>
<!-- This is the subtype of the spell checker. Subtype can describe locales (e.g. en_US, fr_FR...) -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4a5e442..8ac94fb 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3588,8 +3588,6 @@
<public type="attr" name="parentActivityName" />
- <public type="attr" name="supportsSentenceSpellCheck" />
-
<public type="attr" name="importantForAccessibility"/>
</resources>