Loading api/current.txt +3 −5 Original line number Original line Diff line number Diff line Loading @@ -534,7 +534,7 @@ package android { field public static final int imeSubtypeLocale = 16843500; // 0x10102ec field public static final int imeSubtypeLocale = 16843500; // 0x10102ec field public static final int imeSubtypeMode = 16843501; // 0x10102ed field public static final int imeSubtypeMode = 16843501; // 0x10102ed field public static final int immersive = 16843456; // 0x10102c0 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 inAnimation = 16843127; // 0x1010177 field public static final int includeFontPadding = 16843103; // 0x101015f field public static final int includeFontPadding = 16843103; // 0x101015f field public static final int includeInGlobalSearch = 16843374; // 0x101026e field public static final int includeInGlobalSearch = 16843374; // 0x101026e Loading Loading @@ -936,7 +936,6 @@ package android { field public static final int summaryOff = 16843248; // 0x10101f0 field public static final int summaryOff = 16843248; // 0x10101f0 field public static final int summaryOn = 16843247; // 0x10101ef field public static final int summaryOn = 16843247; // 0x10101ef field public static final int supportsRtl = 16843688; // 0x10103a8 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 supportsUploading = 16843419; // 0x101029b field public static final int switchMinWidth = 16843632; // 0x1010370 field public static final int switchMinWidth = 16843632; // 0x1010370 field public static final int switchPadding = 16843633; // 0x1010371 field public static final int switchPadding = 16843633; // 0x1010371 Loading Loading @@ -25425,9 +25424,8 @@ package android.view.textservice { method public void close(); method public void close(); method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int); method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int); method public android.view.textservice.SpellCheckerInfo getSpellChecker(); method public android.view.textservice.SpellCheckerInfo getSpellChecker(); method public void getSuggestions(android.view.textservice.TextInfo, int); method public deprecated void getSuggestions(android.view.textservice.TextInfo, int); method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean); method public deprecated void getSuggestions(android.view.textservice.TextInfo[], int, boolean); method public boolean isSentenceSpellCheckSupported(); method public boolean isSessionDisconnected(); method public boolean isSessionDisconnected(); field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs"; field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs"; } } core/java/android/service/textservice/SpellCheckerService.java +175 −10 Original line number Original line Diff line number Diff line Loading @@ -26,12 +26,18 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IBinder; import android.os.Process; import android.os.Process; import android.os.RemoteException; import android.os.RemoteException; import android.text.TextUtils; import android.text.method.WordIterator; import android.util.Log; import android.util.Log; import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import android.view.textservice.TextInfo; import android.widget.SpellChecker; import java.lang.ref.WeakReference; 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. * SpellCheckerService provides an abstract base class for a spell checker. Loading Loading @@ -92,6 +98,7 @@ public abstract class SpellCheckerService extends Service { */ */ public static abstract class Session { public static abstract class Session { private InternalISpellCheckerSession mInternalSession; private InternalISpellCheckerSession mInternalSession; private volatile SentenceLevelAdapter mSentenceLevelAdapter; /** /** * @hide * @hide Loading Loading @@ -142,8 +149,8 @@ public abstract class SpellCheckerService extends Service { /** /** * Get sentence suggestions for specified texts in an array of TextInfo. * Get sentence suggestions for specified texts in an array of TextInfo. * The default implementation returns an array of SentenceSuggestionsInfo by simply * The default implementation splits the input text to words and returns * calling onGetSuggestions. * {@link SentenceSuggestionsInfo} which contains suggestions for each word. * This function will run on the incoming IPC thread. * This function will run on the incoming IPC thread. * So, this is not called on the main thread, * So, this is not called on the main thread, * but will be called in series on another thread. * but will be called in series on another thread. Loading @@ -156,14 +163,41 @@ public abstract class SpellCheckerService extends Service { */ */ public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { int suggestionsLimit) { final int length = textInfos.length; if (textInfos == null || textInfos.length == 0) { final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length]; return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS; for (int i = 0; i < length; ++i) { } final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit); if (DBG) { si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); Log.d(TAG, "onGetSentenceSuggestionsMultiple: + " + textInfos.length + ", " final int N = textInfos[i].getText().length(); + suggestionsLimit); retval[i] = new SentenceSuggestionsInfo( } new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N}); 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; return retval; } } Loading Loading @@ -290,4 +324,135 @@ public abstract class SpellCheckerService extends Service { return internalSession; 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); } } } } core/java/android/view/textservice/SpellCheckerInfo.java +0 −13 Original line number Original line Diff line number Diff line Loading @@ -45,7 +45,6 @@ public final class SpellCheckerInfo implements Parcelable { private final ResolveInfo mService; private final ResolveInfo mService; private final String mId; private final String mId; private final int mLabel; private final int mLabel; private final boolean mSupportsSentenceSpellCheck; /** /** * The spell checker setting activity's name, used by the system settings to * The spell checker setting activity's name, used by the system settings to Loading Loading @@ -98,9 +97,6 @@ public final class SpellCheckerInfo implements Parcelable { label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0); label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0); settingsActivityComponent = sa.getString( settingsActivityComponent = sa.getString( com.android.internal.R.styleable.SpellChecker_settingsActivity); com.android.internal.R.styleable.SpellChecker_settingsActivity); mSupportsSentenceSpellCheck = sa.getBoolean( com.android.internal.R.styleable.SpellChecker_supportsSentenceSpellCheck, false); sa.recycle(); sa.recycle(); final int depth = parser.getDepth(); final int depth = parser.getDepth(); Loading Loading @@ -142,7 +138,6 @@ public final class SpellCheckerInfo implements Parcelable { */ */ public SpellCheckerInfo(Parcel source) { public SpellCheckerInfo(Parcel source) { mLabel = source.readInt(); mLabel = source.readInt(); mSupportsSentenceSpellCheck = source.readInt() != 0; mId = source.readString(); mId = source.readString(); mSettingsActivityName = source.readString(); mSettingsActivityName = source.readString(); mService = ResolveInfo.CREATOR.createFromParcel(source); mService = ResolveInfo.CREATOR.createFromParcel(source); Loading @@ -157,13 +152,6 @@ public final class SpellCheckerInfo implements Parcelable { return mId; return mId; } } /** * @hide */ public boolean isSentenceSpellCheckSupported() { return mSupportsSentenceSpellCheck; } /** /** * Return the component of the service that implements. * Return the component of the service that implements. */ */ Loading @@ -188,7 +176,6 @@ public final class SpellCheckerInfo implements Parcelable { @Override @Override public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mLabel); dest.writeInt(mLabel); dest.writeInt(mSupportsSentenceSpellCheck ? 1 : 0); dest.writeString(mId); dest.writeString(mId); dest.writeString(mSettingsActivityName); dest.writeString(mSettingsActivityName); mService.writeToParcel(dest, flags); mService.writeToParcel(dest, flags); Loading core/java/android/view/textservice/SpellCheckerSession.java +5 −11 Original line number Original line Diff line number Diff line Loading @@ -91,8 +91,6 @@ public class SpellCheckerSession { * This meta-data must reference an XML resource. * This meta-data must reference an XML resource. **/ **/ public static final String SERVICE_META_DATA = "android.view.textservice.scs"; 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 = 1; private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2; private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2; Loading Loading @@ -191,7 +189,9 @@ public class SpellCheckerSession { * Get candidate strings for a substring of the specified text. * Get candidate strings for a substring of the specified text. * @param textInfo text metadata for a spell checker * @param textInfo text metadata for a spell checker * @param suggestionsLimit the maximum number of suggestions that will be returned * @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) { public void getSuggestions(TextInfo textInfo, int suggestionsLimit) { getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false); getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false); } } Loading @@ -201,13 +201,14 @@ public class SpellCheckerSession { * @param textInfos an array of text metadata for a spell checker * @param textInfos an array of text metadata for a spell checker * @param suggestionsLimit the maximum number of suggestions that will be returned * @param suggestionsLimit the maximum number of suggestions that will be returned * @param sequentialWords true if textInfos can be treated as sequential words. * @param sequentialWords true if textInfos can be treated as sequential words. * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead */ */ @Deprecated public void getSuggestions( public void getSuggestions( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { if (DBG) { if (DBG) { Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); } } // TODO: Handle multiple words suggestions by using WordBreakIterator mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( textInfos, suggestionsLimit, sequentialWords); textInfos, suggestionsLimit, sequentialWords); } } Loading Loading @@ -281,7 +282,7 @@ public class SpellCheckerSession { break; break; case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: if (DBG) { if (DBG) { Log.w(TAG, "Get suggestions from the spell checker."); Log.w(TAG, "Get sentence suggestions from the spell checker."); } } try { try { session.onGetSentenceSuggestionsMultiple( session.onGetSentenceSuggestionsMultiple( Loading Loading @@ -492,11 +493,4 @@ public class SpellCheckerSession { public ISpellCheckerSessionListener getSpellCheckerSessionListener() { public ISpellCheckerSessionListener getSpellCheckerSessionListener() { return mSpellCheckerSessionListenerImpl; return mSpellCheckerSessionListenerImpl; } } /** * @return true if the spell checker supports sentence level spell checking APIs */ public boolean isSentenceSpellCheckSupported() { return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK); } } } core/java/android/widget/SpellChecker.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -116,7 +116,7 @@ public class SpellChecker implements SpellCheckerSessionListener { null /* Bundle not currently used by the textServicesManager */, null /* Bundle not currently used by the textServicesManager */, mCurrentLocale, this, mCurrentLocale, this, false /* means any available languages from current spell checker */); false /* means any available languages from current spell checker */); mIsSentenceSpellCheckSupported = mSpellCheckerSession.isSentenceSpellCheckSupported(); mIsSentenceSpellCheckSupported = true; } } // Restore SpellCheckSpans in pool // Restore SpellCheckSpans in pool Loading Loading
api/current.txt +3 −5 Original line number Original line Diff line number Diff line Loading @@ -534,7 +534,7 @@ package android { field public static final int imeSubtypeLocale = 16843500; // 0x10102ec field public static final int imeSubtypeLocale = 16843500; // 0x10102ec field public static final int imeSubtypeMode = 16843501; // 0x10102ed field public static final int imeSubtypeMode = 16843501; // 0x10102ed field public static final int immersive = 16843456; // 0x10102c0 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 inAnimation = 16843127; // 0x1010177 field public static final int includeFontPadding = 16843103; // 0x101015f field public static final int includeFontPadding = 16843103; // 0x101015f field public static final int includeInGlobalSearch = 16843374; // 0x101026e field public static final int includeInGlobalSearch = 16843374; // 0x101026e Loading Loading @@ -936,7 +936,6 @@ package android { field public static final int summaryOff = 16843248; // 0x10101f0 field public static final int summaryOff = 16843248; // 0x10101f0 field public static final int summaryOn = 16843247; // 0x10101ef field public static final int summaryOn = 16843247; // 0x10101ef field public static final int supportsRtl = 16843688; // 0x10103a8 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 supportsUploading = 16843419; // 0x101029b field public static final int switchMinWidth = 16843632; // 0x1010370 field public static final int switchMinWidth = 16843632; // 0x1010370 field public static final int switchPadding = 16843633; // 0x1010371 field public static final int switchPadding = 16843633; // 0x1010371 Loading Loading @@ -25425,9 +25424,8 @@ package android.view.textservice { method public void close(); method public void close(); method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int); method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int); method public android.view.textservice.SpellCheckerInfo getSpellChecker(); method public android.view.textservice.SpellCheckerInfo getSpellChecker(); method public void getSuggestions(android.view.textservice.TextInfo, int); method public deprecated void getSuggestions(android.view.textservice.TextInfo, int); method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean); method public deprecated void getSuggestions(android.view.textservice.TextInfo[], int, boolean); method public boolean isSentenceSpellCheckSupported(); method public boolean isSessionDisconnected(); method public boolean isSessionDisconnected(); field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs"; field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs"; } }
core/java/android/service/textservice/SpellCheckerService.java +175 −10 Original line number Original line Diff line number Diff line Loading @@ -26,12 +26,18 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IBinder; import android.os.Process; import android.os.Process; import android.os.RemoteException; import android.os.RemoteException; import android.text.TextUtils; import android.text.method.WordIterator; import android.util.Log; import android.util.Log; import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import android.view.textservice.TextInfo; import android.widget.SpellChecker; import java.lang.ref.WeakReference; 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. * SpellCheckerService provides an abstract base class for a spell checker. Loading Loading @@ -92,6 +98,7 @@ public abstract class SpellCheckerService extends Service { */ */ public static abstract class Session { public static abstract class Session { private InternalISpellCheckerSession mInternalSession; private InternalISpellCheckerSession mInternalSession; private volatile SentenceLevelAdapter mSentenceLevelAdapter; /** /** * @hide * @hide Loading Loading @@ -142,8 +149,8 @@ public abstract class SpellCheckerService extends Service { /** /** * Get sentence suggestions for specified texts in an array of TextInfo. * Get sentence suggestions for specified texts in an array of TextInfo. * The default implementation returns an array of SentenceSuggestionsInfo by simply * The default implementation splits the input text to words and returns * calling onGetSuggestions. * {@link SentenceSuggestionsInfo} which contains suggestions for each word. * This function will run on the incoming IPC thread. * This function will run on the incoming IPC thread. * So, this is not called on the main thread, * So, this is not called on the main thread, * but will be called in series on another thread. * but will be called in series on another thread. Loading @@ -156,14 +163,41 @@ public abstract class SpellCheckerService extends Service { */ */ public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { int suggestionsLimit) { final int length = textInfos.length; if (textInfos == null || textInfos.length == 0) { final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length]; return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS; for (int i = 0; i < length; ++i) { } final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit); if (DBG) { si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); Log.d(TAG, "onGetSentenceSuggestionsMultiple: + " + textInfos.length + ", " final int N = textInfos[i].getText().length(); + suggestionsLimit); retval[i] = new SentenceSuggestionsInfo( } new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N}); 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; return retval; } } Loading Loading @@ -290,4 +324,135 @@ public abstract class SpellCheckerService extends Service { return internalSession; 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); } } } }
core/java/android/view/textservice/SpellCheckerInfo.java +0 −13 Original line number Original line Diff line number Diff line Loading @@ -45,7 +45,6 @@ public final class SpellCheckerInfo implements Parcelable { private final ResolveInfo mService; private final ResolveInfo mService; private final String mId; private final String mId; private final int mLabel; private final int mLabel; private final boolean mSupportsSentenceSpellCheck; /** /** * The spell checker setting activity's name, used by the system settings to * The spell checker setting activity's name, used by the system settings to Loading Loading @@ -98,9 +97,6 @@ public final class SpellCheckerInfo implements Parcelable { label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0); label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0); settingsActivityComponent = sa.getString( settingsActivityComponent = sa.getString( com.android.internal.R.styleable.SpellChecker_settingsActivity); com.android.internal.R.styleable.SpellChecker_settingsActivity); mSupportsSentenceSpellCheck = sa.getBoolean( com.android.internal.R.styleable.SpellChecker_supportsSentenceSpellCheck, false); sa.recycle(); sa.recycle(); final int depth = parser.getDepth(); final int depth = parser.getDepth(); Loading Loading @@ -142,7 +138,6 @@ public final class SpellCheckerInfo implements Parcelable { */ */ public SpellCheckerInfo(Parcel source) { public SpellCheckerInfo(Parcel source) { mLabel = source.readInt(); mLabel = source.readInt(); mSupportsSentenceSpellCheck = source.readInt() != 0; mId = source.readString(); mId = source.readString(); mSettingsActivityName = source.readString(); mSettingsActivityName = source.readString(); mService = ResolveInfo.CREATOR.createFromParcel(source); mService = ResolveInfo.CREATOR.createFromParcel(source); Loading @@ -157,13 +152,6 @@ public final class SpellCheckerInfo implements Parcelable { return mId; return mId; } } /** * @hide */ public boolean isSentenceSpellCheckSupported() { return mSupportsSentenceSpellCheck; } /** /** * Return the component of the service that implements. * Return the component of the service that implements. */ */ Loading @@ -188,7 +176,6 @@ public final class SpellCheckerInfo implements Parcelable { @Override @Override public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mLabel); dest.writeInt(mLabel); dest.writeInt(mSupportsSentenceSpellCheck ? 1 : 0); dest.writeString(mId); dest.writeString(mId); dest.writeString(mSettingsActivityName); dest.writeString(mSettingsActivityName); mService.writeToParcel(dest, flags); mService.writeToParcel(dest, flags); Loading
core/java/android/view/textservice/SpellCheckerSession.java +5 −11 Original line number Original line Diff line number Diff line Loading @@ -91,8 +91,6 @@ public class SpellCheckerSession { * This meta-data must reference an XML resource. * This meta-data must reference an XML resource. **/ **/ public static final String SERVICE_META_DATA = "android.view.textservice.scs"; 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 = 1; private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2; private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2; Loading Loading @@ -191,7 +189,9 @@ public class SpellCheckerSession { * Get candidate strings for a substring of the specified text. * Get candidate strings for a substring of the specified text. * @param textInfo text metadata for a spell checker * @param textInfo text metadata for a spell checker * @param suggestionsLimit the maximum number of suggestions that will be returned * @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) { public void getSuggestions(TextInfo textInfo, int suggestionsLimit) { getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false); getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false); } } Loading @@ -201,13 +201,14 @@ public class SpellCheckerSession { * @param textInfos an array of text metadata for a spell checker * @param textInfos an array of text metadata for a spell checker * @param suggestionsLimit the maximum number of suggestions that will be returned * @param suggestionsLimit the maximum number of suggestions that will be returned * @param sequentialWords true if textInfos can be treated as sequential words. * @param sequentialWords true if textInfos can be treated as sequential words. * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead */ */ @Deprecated public void getSuggestions( public void getSuggestions( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { if (DBG) { if (DBG) { Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId()); } } // TODO: Handle multiple words suggestions by using WordBreakIterator mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( textInfos, suggestionsLimit, sequentialWords); textInfos, suggestionsLimit, sequentialWords); } } Loading Loading @@ -281,7 +282,7 @@ public class SpellCheckerSession { break; break; case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: if (DBG) { if (DBG) { Log.w(TAG, "Get suggestions from the spell checker."); Log.w(TAG, "Get sentence suggestions from the spell checker."); } } try { try { session.onGetSentenceSuggestionsMultiple( session.onGetSentenceSuggestionsMultiple( Loading Loading @@ -492,11 +493,4 @@ public class SpellCheckerSession { public ISpellCheckerSessionListener getSpellCheckerSessionListener() { public ISpellCheckerSessionListener getSpellCheckerSessionListener() { return mSpellCheckerSessionListenerImpl; return mSpellCheckerSessionListenerImpl; } } /** * @return true if the spell checker supports sentence level spell checking APIs */ public boolean isSentenceSpellCheckSupported() { return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK); } } }
core/java/android/widget/SpellChecker.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -116,7 +116,7 @@ public class SpellChecker implements SpellCheckerSessionListener { null /* Bundle not currently used by the textServicesManager */, null /* Bundle not currently used by the textServicesManager */, mCurrentLocale, this, mCurrentLocale, this, false /* means any available languages from current spell checker */); false /* means any available languages from current spell checker */); mIsSentenceSpellCheckSupported = mSpellCheckerSession.isSentenceSpellCheckSupported(); mIsSentenceSpellCheckSupported = true; } } // Restore SpellCheckSpans in pool // Restore SpellCheckSpans in pool Loading