Merge "Get rid of "isSentenceLevelSpellCheckSupported""
This commit is contained in:
@@ -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
|
||||
@@ -25520,9 +25519,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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -157,13 +152,6 @@ public final class SpellCheckerInfo implements Parcelable {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public boolean isSentenceSpellCheckSupported() {
|
||||
return mSupportsSentenceSpellCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the component of the service that implements.
|
||||
*/
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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...) -->
|
||||
|
||||
@@ -3588,8 +3588,6 @@
|
||||
|
||||
<public type="attr" name="parentActivityName" />
|
||||
|
||||
<public type="attr" name="supportsSentenceSpellCheck" />
|
||||
|
||||
<public type="attr" name="importantForAccessibility"/>
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user