Skip to content

Commit fbf7e1f

Browse files
satok16Android (Google) Code Review
authored andcommitted
Merge "Get rid of "isSentenceLevelSpellCheckSupported""
2 parents 55a2872 + c7ee1b9 commit fbf7e1f

File tree

7 files changed

+184
-44
lines changed

7 files changed

+184
-44
lines changed

api/current.txt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ package android {
534534
field public static final int imeSubtypeLocale = 16843500; // 0x10102ec
535535
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
536536
field public static final int immersive = 16843456; // 0x10102c0
537-
field public static final int importantForAccessibility = 16843699; // 0x10103b3
537+
field public static final int importantForAccessibility = 16843698; // 0x10103b2
538538
field public static final int inAnimation = 16843127; // 0x1010177
539539
field public static final int includeFontPadding = 16843103; // 0x101015f
540540
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -936,7 +936,6 @@ package android {
936936
field public static final int summaryOff = 16843248; // 0x10101f0
937937
field public static final int summaryOn = 16843247; // 0x10101ef
938938
field public static final int supportsRtl = 16843688; // 0x10103a8
939-
field public static final int supportsSentenceSpellCheck = 16843698; // 0x10103b2
940939
field public static final int supportsUploading = 16843419; // 0x101029b
941940
field public static final int switchMinWidth = 16843632; // 0x1010370
942941
field public static final int switchPadding = 16843633; // 0x1010371
@@ -25520,9 +25519,8 @@ package android.view.textservice {
2552025519
method public void close();
2552125520
method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int);
2552225521
method public android.view.textservice.SpellCheckerInfo getSpellChecker();
25523-
method public void getSuggestions(android.view.textservice.TextInfo, int);
25524-
method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
25525-
method public boolean isSentenceSpellCheckSupported();
25522+
method public deprecated void getSuggestions(android.view.textservice.TextInfo, int);
25523+
method public deprecated void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
2552625524
method public boolean isSessionDisconnected();
2552725525
field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs";
2552825526
}

core/java/android/service/textservice/SpellCheckerService.java

Lines changed: 175 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@
2626
import android.os.IBinder;
2727
import android.os.Process;
2828
import android.os.RemoteException;
29+
import android.text.TextUtils;
30+
import android.text.method.WordIterator;
2931
import android.util.Log;
3032
import android.view.textservice.SentenceSuggestionsInfo;
3133
import android.view.textservice.SuggestionsInfo;
3234
import android.view.textservice.TextInfo;
35+
import android.widget.SpellChecker;
3336

3437
import java.lang.ref.WeakReference;
38+
import java.text.BreakIterator;
39+
import java.util.ArrayList;
40+
import java.util.Locale;
3541

3642
/**
3743
* SpellCheckerService provides an abstract base class for a spell checker.
@@ -92,6 +98,7 @@ public final IBinder onBind(final Intent intent) {
9298
*/
9399
public static abstract class Session {
94100
private InternalISpellCheckerSession mInternalSession;
101+
private volatile SentenceLevelAdapter mSentenceLevelAdapter;
95102

96103
/**
97104
* @hide
@@ -142,8 +149,8 @@ public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
142149

143150
/**
144151
* Get sentence suggestions for specified texts in an array of TextInfo.
145-
* The default implementation returns an array of SentenceSuggestionsInfo by simply
146-
* calling onGetSuggestions.
152+
* The default implementation splits the input text to words and returns
153+
* {@link SentenceSuggestionsInfo} which contains suggestions for each word.
147154
* This function will run on the incoming IPC thread.
148155
* So, this is not called on the main thread,
149156
* but will be called in series on another thread.
@@ -156,14 +163,41 @@ public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
156163
*/
157164
public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
158165
int suggestionsLimit) {
159-
final int length = textInfos.length;
160-
final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length];
161-
for (int i = 0; i < length; ++i) {
162-
final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit);
163-
si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence());
164-
final int N = textInfos[i].getText().length();
165-
retval[i] = new SentenceSuggestionsInfo(
166-
new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N});
166+
if (textInfos == null || textInfos.length == 0) {
167+
return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
168+
}
169+
if (DBG) {
170+
Log.d(TAG, "onGetSentenceSuggestionsMultiple: + " + textInfos.length + ", "
171+
+ suggestionsLimit);
172+
}
173+
if (mSentenceLevelAdapter == null) {
174+
synchronized(this) {
175+
if (mSentenceLevelAdapter == null) {
176+
final String localeStr = getLocale();
177+
if (!TextUtils.isEmpty(localeStr)) {
178+
mSentenceLevelAdapter = new SentenceLevelAdapter(new Locale(localeStr));
179+
}
180+
}
181+
}
182+
}
183+
if (mSentenceLevelAdapter == null) {
184+
return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
185+
}
186+
final int infosSize = textInfos.length;
187+
final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize];
188+
for (int i = 0; i < infosSize; ++i) {
189+
final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams =
190+
mSentenceLevelAdapter.getSplitWords(textInfos[i]);
191+
final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems =
192+
textInfoParams.mItems;
193+
final int itemsSize = mItems.size();
194+
final TextInfo[] splitTextInfos = new TextInfo[itemsSize];
195+
for (int j = 0; j < itemsSize; ++j) {
196+
splitTextInfos[j] = mItems.get(j).mTextInfo;
197+
}
198+
retval[i] = SentenceLevelAdapter.reconstructSuggestions(
199+
textInfoParams, onGetSuggestionsMultiple(
200+
splitTextInfos, suggestionsLimit, true));
167201
}
168202
return retval;
169203
}
@@ -290,4 +324,135 @@ public ISpellCheckerSession getISpellCheckerSession(
290324
return internalSession;
291325
}
292326
}
327+
328+
/**
329+
* Adapter class to accommodate word level spell checking APIs to sentence level spell checking
330+
* APIs used in
331+
* {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)}
332+
*/
333+
private static class SentenceLevelAdapter {
334+
public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS =
335+
new SentenceSuggestionsInfo[] {};
336+
private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null);
337+
/**
338+
* Container for split TextInfo parameters
339+
*/
340+
public static class SentenceWordItem {
341+
public final TextInfo mTextInfo;
342+
public final int mStart;
343+
public final int mLength;
344+
public SentenceWordItem(TextInfo ti, int start, int end) {
345+
mTextInfo = ti;
346+
mStart = start;
347+
mLength = end - start;
348+
}
349+
}
350+
351+
/**
352+
* Container for originally queried TextInfo and parameters
353+
*/
354+
public static class SentenceTextInfoParams {
355+
final TextInfo mOriginalTextInfo;
356+
final ArrayList<SentenceWordItem> mItems;
357+
final int mSize;
358+
public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) {
359+
mOriginalTextInfo = ti;
360+
mItems = items;
361+
mSize = items.size();
362+
}
363+
}
364+
365+
private final WordIterator mWordIterator;
366+
public SentenceLevelAdapter(Locale locale) {
367+
mWordIterator = new WordIterator(locale);
368+
}
369+
370+
private SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) {
371+
final WordIterator wordIterator = mWordIterator;
372+
final CharSequence originalText = originalTextInfo.getText();
373+
final int cookie = originalTextInfo.getCookie();
374+
final int start = 0;
375+
final int end = originalText.length();
376+
final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>();
377+
wordIterator.setCharSequence(originalText, 0, originalText.length());
378+
int wordEnd = wordIterator.following(start);
379+
int wordStart = wordIterator.getBeginning(wordEnd);
380+
if (DBG) {
381+
Log.d(TAG, "iterator: break: ---- 1st word start = " + wordStart + ", end = "
382+
+ wordEnd + "\n" + originalText);
383+
}
384+
while (wordStart <= end && wordEnd != BreakIterator.DONE
385+
&& wordStart != BreakIterator.DONE) {
386+
if (wordEnd >= start && wordEnd > wordStart) {
387+
final String query = originalText.subSequence(wordStart, wordEnd).toString();
388+
final TextInfo ti = new TextInfo(query, cookie, query.hashCode());
389+
wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
390+
if (DBG) {
391+
Log.d(TAG, "Adapter: word (" + (wordItems.size() - 1) + ") " + query);
392+
}
393+
}
394+
wordEnd = wordIterator.following(wordEnd);
395+
if (wordEnd == BreakIterator.DONE) {
396+
break;
397+
}
398+
wordStart = wordIterator.getBeginning(wordEnd);
399+
}
400+
if (originalText.length() >= SpellChecker.WORD_ITERATOR_INTERVAL
401+
&& wordItems.size() >= 2) {
402+
if (DBG) {
403+
Log.w(TAG, "Remove possibly divided word: "
404+
+ wordItems.get(0).mTextInfo.getText());
405+
}
406+
wordItems.remove(0);
407+
}
408+
return new SentenceTextInfoParams(originalTextInfo, wordItems);
409+
}
410+
411+
public static SentenceSuggestionsInfo reconstructSuggestions(
412+
SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) {
413+
if (results == null || results.length == 0) {
414+
return null;
415+
}
416+
if (DBG) {
417+
Log.w(TAG, "Adapter: onGetSuggestions: got " + results.length);
418+
}
419+
if (originalTextInfoParams == null) {
420+
if (DBG) {
421+
Log.w(TAG, "Adapter: originalTextInfoParams is null.");
422+
}
423+
return null;
424+
}
425+
final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie();
426+
final int originalSequence =
427+
originalTextInfoParams.mOriginalTextInfo.getSequence();
428+
429+
final int querySize = originalTextInfoParams.mSize;
430+
final int[] offsets = new int[querySize];
431+
final int[] lengths = new int[querySize];
432+
final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize];
433+
for (int i = 0; i < querySize; ++i) {
434+
final SentenceWordItem item = originalTextInfoParams.mItems.get(i);
435+
SuggestionsInfo result = null;
436+
for (int j = 0; j < results.length; ++j) {
437+
final SuggestionsInfo cur = results[j];
438+
if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) {
439+
result = cur;
440+
result.setCookieAndSequence(originalCookie, originalSequence);
441+
break;
442+
}
443+
}
444+
offsets[i] = item.mStart;
445+
lengths[i] = item.mLength;
446+
reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO;
447+
if (DBG) {
448+
final int size = reconstructedSuggestions[i].getSuggestionsCount();
449+
Log.w(TAG, "reconstructedSuggestions(" + i + ")" + size + ", first = "
450+
+ (size > 0 ? reconstructedSuggestions[i].getSuggestionAt(0)
451+
: "<none>") + ", offset = " + offsets[i] + ", length = "
452+
+ lengths[i]);
453+
}
454+
}
455+
return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths);
456+
}
457+
}
293458
}

core/java/android/view/textservice/SpellCheckerInfo.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public final class SpellCheckerInfo implements Parcelable {
4545
private final ResolveInfo mService;
4646
private final String mId;
4747
private final int mLabel;
48-
private final boolean mSupportsSentenceSpellCheck;
4948

5049
/**
5150
* The spell checker setting activity's name, used by the system settings to
@@ -98,9 +97,6 @@ public SpellCheckerInfo(Context context, ResolveInfo service)
9897
label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0);
9998
settingsActivityComponent = sa.getString(
10099
com.android.internal.R.styleable.SpellChecker_settingsActivity);
101-
mSupportsSentenceSpellCheck = sa.getBoolean(
102-
com.android.internal.R.styleable.SpellChecker_supportsSentenceSpellCheck,
103-
false);
104100
sa.recycle();
105101

106102
final int depth = parser.getDepth();
@@ -142,7 +138,6 @@ public SpellCheckerInfo(Context context, ResolveInfo service)
142138
*/
143139
public SpellCheckerInfo(Parcel source) {
144140
mLabel = source.readInt();
145-
mSupportsSentenceSpellCheck = source.readInt() != 0;
146141
mId = source.readString();
147142
mSettingsActivityName = source.readString();
148143
mService = ResolveInfo.CREATOR.createFromParcel(source);
@@ -157,13 +152,6 @@ public String getId() {
157152
return mId;
158153
}
159154

160-
/**
161-
* @hide
162-
*/
163-
public boolean isSentenceSpellCheckSupported() {
164-
return mSupportsSentenceSpellCheck;
165-
}
166-
167155
/**
168156
* Return the component of the service that implements.
169157
*/
@@ -188,7 +176,6 @@ public String getPackageName() {
188176
@Override
189177
public void writeToParcel(Parcel dest, int flags) {
190178
dest.writeInt(mLabel);
191-
dest.writeInt(mSupportsSentenceSpellCheck ? 1 : 0);
192179
dest.writeString(mId);
193180
dest.writeString(mSettingsActivityName);
194181
mService.writeToParcel(dest, flags);

core/java/android/view/textservice/SpellCheckerSession.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ public class SpellCheckerSession {
9191
* This meta-data must reference an XML resource.
9292
**/
9393
public static final String SERVICE_META_DATA = "android.view.textservice.scs";
94-
private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck";
95-
9694

9795
private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
9896
private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
@@ -191,7 +189,9 @@ public void getSentenceSuggestions(TextInfo[] textInfos, int suggestionsLimit) {
191189
* Get candidate strings for a substring of the specified text.
192190
* @param textInfo text metadata for a spell checker
193191
* @param suggestionsLimit the maximum number of suggestions that will be returned
192+
* @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
194193
*/
194+
@Deprecated
195195
public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
196196
getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false);
197197
}
@@ -201,13 +201,14 @@ public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
201201
* @param textInfos an array of text metadata for a spell checker
202202
* @param suggestionsLimit the maximum number of suggestions that will be returned
203203
* @param sequentialWords true if textInfos can be treated as sequential words.
204+
* @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
204205
*/
206+
@Deprecated
205207
public void getSuggestions(
206208
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
207209
if (DBG) {
208210
Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId());
209211
}
210-
// TODO: Handle multiple words suggestions by using WordBreakIterator
211212
mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
212213
textInfos, suggestionsLimit, sequentialWords);
213214
}
@@ -281,7 +282,7 @@ private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
281282
break;
282283
case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
283284
if (DBG) {
284-
Log.w(TAG, "Get suggestions from the spell checker.");
285+
Log.w(TAG, "Get sentence suggestions from the spell checker.");
285286
}
286287
try {
287288
session.onGetSentenceSuggestionsMultiple(
@@ -492,11 +493,4 @@ public ITextServicesSessionListener getTextServicesSessionListener() {
492493
public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
493494
return mSpellCheckerSessionListenerImpl;
494495
}
495-
496-
/**
497-
* @return true if the spell checker supports sentence level spell checking APIs
498-
*/
499-
public boolean isSentenceSpellCheckSupported() {
500-
return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK);
501-
}
502496
}

core/java/android/widget/SpellChecker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private void resetSession() {
116116
null /* Bundle not currently used by the textServicesManager */,
117117
mCurrentLocale, this,
118118
false /* means any available languages from current spell checker */);
119-
mIsSentenceSpellCheckSupported = mSpellCheckerSession.isSentenceSpellCheckSupported();
119+
mIsSentenceSpellCheckSupported = true;
120120
}
121121

122122
// Restore SpellCheckSpans in pool

core/res/res/values/attrs.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,8 +2367,6 @@
23672367
<!-- Component name of an activity that allows the user to modify
23682368
the settings for this service. -->
23692369
<attr name="settingsActivity"/>
2370-
<!-- Set true when the spell checker supports sentence level spell checking. -->
2371-
<attr name="supportsSentenceSpellCheck" format="boolean" />
23722370
</declare-styleable>
23732371

23742372
<!-- This is the subtype of the spell checker. Subtype can describe locales (e.g. en_US, fr_FR...) -->

core/res/res/values/public.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3588,8 +3588,6 @@
35883588

35893589
<public type="attr" name="parentActivityName" />
35903590

3591-
<public type="attr" name="supportsSentenceSpellCheck" />
3592-
35933591
<public type="attr" name="importantForAccessibility"/>
35943592

35953593
</resources>

0 commit comments

Comments
 (0)