diff options
-rw-r--r-- | api/current.txt | 8 | ||||
-rw-r--r-- | core/java/android/service/textservice/SpellCheckerService.java | 185 | ||||
-rw-r--r-- | core/java/android/view/textservice/SpellCheckerInfo.java | 13 | ||||
-rw-r--r-- | core/java/android/view/textservice/SpellCheckerSession.java | 16 | ||||
-rw-r--r-- | core/java/android/widget/SpellChecker.java | 2 | ||||
-rwxr-xr-x | core/res/res/values/attrs.xml | 2 | ||||
-rw-r--r-- | core/res/res/values/public.xml | 2 |
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> |