Merge "Add API to differentiate grammar suggestions."

This commit is contained in:
Kohsuke Yatoh
2020-10-07 03:59:25 +00:00
committed by Android (Google) Code Review
10 changed files with 100 additions and 22 deletions

View File

@@ -50874,6 +50874,7 @@ package android.text.style {
field @NonNull public static final android.os.Parcelable.Creator<android.text.style.SuggestionSpan> CREATOR;
field public static final int FLAG_AUTO_CORRECTION = 4; // 0x4
field public static final int FLAG_EASY_CORRECT = 1; // 0x1
field public static final int FLAG_GRAMMAR_ERROR = 8; // 0x8
field public static final int FLAG_MISSPELLED = 2; // 0x2
field public static final int SUGGESTIONS_MAX_SIZE = 5; // 0x5
field @Deprecated public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
@@ -58248,6 +58249,7 @@ package android.view.textservice {
field @NonNull public static final android.os.Parcelable.Creator<android.view.textservice.SuggestionsInfo> CREATOR;
field public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 4; // 0x4
field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
field public static final int RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR = 8; // 0x8
field public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 2; // 0x2
}

View File

@@ -70,6 +70,12 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
*/
public static final int FLAG_AUTO_CORRECTION = 0x0004;
/**
* Sets this flag if the suggestions apply to a grammar error. This type of suggestion is
* rendered differently to highlight the error.
*/
public static final int FLAG_GRAMMAR_ERROR = 0x0008;
/**
* This action is deprecated in {@link android.os.Build.VERSION_CODES#Q}.
*
@@ -136,6 +142,9 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
private float mAutoCorrectionUnderlineThickness;
private int mAutoCorrectionUnderlineColor;
private float mGrammarErrorUnderlineThickness;
private int mGrammarErrorUnderlineColor;
/**
* @param context Context for the application
* @param suggestions Suggestions for the string under the span
@@ -190,9 +199,11 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
private void initStyle(Context context) {
if (context == null) {
mMisspelledUnderlineThickness = 0;
mGrammarErrorUnderlineThickness = 0;
mEasyCorrectUnderlineThickness = 0;
mAutoCorrectionUnderlineThickness = 0;
mMisspelledUnderlineColor = Color.BLACK;
mGrammarErrorUnderlineColor = Color.BLACK;
mEasyCorrectUnderlineColor = Color.BLACK;
mAutoCorrectionUnderlineColor = Color.BLACK;
return;
@@ -206,6 +217,14 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
mMisspelledUnderlineColor = typedArray.getColor(
com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK);
defStyleAttr = com.android.internal.R.attr.textAppearanceGrammarErrorSuggestion;
typedArray = context.obtainStyledAttributes(
null, com.android.internal.R.styleable.SuggestionSpan, defStyleAttr, 0);
mGrammarErrorUnderlineThickness = typedArray.getDimension(
com.android.internal.R.styleable.SuggestionSpan_textUnderlineThickness, 0);
mGrammarErrorUnderlineColor = typedArray.getColor(
com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK);
defStyleAttr = com.android.internal.R.attr.textAppearanceEasyCorrectSuggestion;
typedArray = context.obtainStyledAttributes(
null, com.android.internal.R.styleable.SuggestionSpan, defStyleAttr, 0);
@@ -235,6 +254,8 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
mMisspelledUnderlineThickness = src.readFloat();
mAutoCorrectionUnderlineColor = src.readInt();
mAutoCorrectionUnderlineThickness = src.readFloat();
mGrammarErrorUnderlineColor = src.readInt();
mGrammarErrorUnderlineThickness = src.readFloat();
}
/**
@@ -313,6 +334,8 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
dest.writeFloat(mMisspelledUnderlineThickness);
dest.writeInt(mAutoCorrectionUnderlineColor);
dest.writeFloat(mAutoCorrectionUnderlineThickness);
dest.writeInt(mGrammarErrorUnderlineColor);
dest.writeFloat(mGrammarErrorUnderlineThickness);
}
@Override
@@ -362,13 +385,19 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
final boolean easy = (mFlags & FLAG_EASY_CORRECT) != 0;
final boolean autoCorrection = (mFlags & FLAG_AUTO_CORRECTION) != 0;
final boolean grammarError = (mFlags & FLAG_GRAMMAR_ERROR) != 0;
if (easy) {
if (!misspelled) {
if (!misspelled && !grammarError) {
tp.setUnderlineText(mEasyCorrectUnderlineColor, mEasyCorrectUnderlineThickness);
} else if (tp.underlineColor == 0) {
// Spans are rendered in an arbitrary order. Since misspelled is less prioritary
// than just easy, do not apply misspelled if an easy (or a mispelled) has been set
tp.setUnderlineText(mMisspelledUnderlineColor, mMisspelledUnderlineThickness);
if (grammarError) {
tp.setUnderlineText(
mGrammarErrorUnderlineColor, mGrammarErrorUnderlineThickness);
} else {
tp.setUnderlineText(mMisspelledUnderlineColor, mMisspelledUnderlineThickness);
}
}
} else if (autoCorrection) {
tp.setUnderlineText(mAutoCorrectionUnderlineColor, mAutoCorrectionUnderlineThickness);
@@ -384,11 +413,14 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
final boolean easy = (mFlags & FLAG_EASY_CORRECT) != 0;
final boolean autoCorrection = (mFlags & FLAG_AUTO_CORRECTION) != 0;
final boolean grammarError = (mFlags & FLAG_GRAMMAR_ERROR) != 0;
if (easy) {
if (!misspelled) {
return mEasyCorrectUnderlineColor;
} else {
if (grammarError) {
return mGrammarErrorUnderlineColor;
} else if (misspelled) {
return mMisspelledUnderlineColor;
} else {
return mEasyCorrectUnderlineColor;
}
} else if (autoCorrection) {
return mAutoCorrectionUnderlineColor;

View File

@@ -45,6 +45,14 @@ public final class SuggestionsInfo implements Parcelable {
* the result suggestions include highly recommended ones.
*/
public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004;
/**
* Flag of the attributes of the suggestions that can be obtained by
* {@link #getSuggestionsAttributes}: this tells that the text service thinks the requested
* sentence contains a grammar error.
*/
public static final int RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR = 0x0008;
private final int mSuggestionsAttributes;
private final String[] mSuggestions;
private final boolean mSuggestionsAvailable;

View File

@@ -186,6 +186,9 @@ public class Editor {
private static final int MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START = 50;
private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
private static final int FLAG_MISSPELLED_OR_GRAMMAR_ERROR =
SuggestionSpan.FLAG_MISSPELLED | SuggestionSpan.FLAG_GRAMMAR_ERROR;
@IntDef({MagnifierHandleTrigger.SELECTION_START,
MagnifierHandleTrigger.SELECTION_END,
MagnifierHandleTrigger.INSERTION})
@@ -1552,7 +1555,7 @@ public class Editor {
for (int i = 0; i < suggestionSpans.length; i++) {
int flags = suggestionSpans[i].getFlags();
if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
&& (flags & SuggestionSpan.FLAG_MISSPELLED) == 0) {
&& (flags & FLAG_MISSPELLED_OR_GRAMMAR_ERROR) == 0) {
flags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
suggestionSpans[i].setFlags(flags);
}
@@ -3064,8 +3067,9 @@ public class Editor {
// Remove potential misspelled flags
int suggestionSpanFlags = suggestionSpan.getFlags();
if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
if ((suggestionSpanFlags & FLAG_MISSPELLED_OR_GRAMMAR_ERROR) != 0) {
suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
suggestionSpanFlags &= ~SuggestionSpan.FLAG_GRAMMAR_ERROR;
suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
suggestionSpan.setFlags(suggestionSpanFlags);
}
@@ -3601,19 +3605,29 @@ public class Editor {
final int flag1 = span1.getFlags();
final int flag2 = span2.getFlags();
if (flag1 != flag2) {
// The order here should match what is used in updateDrawState
final boolean easy1 = (flag1 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
final boolean easy2 = (flag2 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
final boolean misspelled1 = (flag1 & SuggestionSpan.FLAG_MISSPELLED) != 0;
final boolean misspelled2 = (flag2 & SuggestionSpan.FLAG_MISSPELLED) != 0;
if (easy1 && !misspelled1) return -1;
if (easy2 && !misspelled2) return 1;
if (misspelled1) return -1;
if (misspelled2) return 1;
// Compare so that the order will be: easy -> misspelled -> grammarError
int easy = compareFlag(SuggestionSpan.FLAG_EASY_CORRECT, flag1, flag2);
if (easy != 0) return easy;
int misspelled = compareFlag(SuggestionSpan.FLAG_MISSPELLED, flag1, flag2);
if (misspelled != 0) return misspelled;
int grammarError = compareFlag(SuggestionSpan.FLAG_GRAMMAR_ERROR, flag1, flag2);
if (grammarError != 0) return grammarError;
}
return mSpansLengths.get(span1).intValue() - mSpansLengths.get(span2).intValue();
}
/*
* Returns -1 if flags1 has flagToCompare but flags2 does not.
* Returns 1 if flags2 has flagToCompare but flags1 does not.
* Otherwise, returns 0.
*/
private int compareFlag(int flagToCompare, int flags1, int flags2) {
boolean hasFlag1 = (flags1 & flagToCompare) != 0;
boolean hasFlag2 = (flags2 & flagToCompare) != 0;
if (hasFlag1 == hasFlag2) return 0;
return hasFlag1 ? -1 : 1;
}
}
/**
@@ -3632,8 +3646,9 @@ public class Editor {
mSpansLengths.put(suggestionSpan, Integer.valueOf(end - start));
}
// The suggestions are sorted according to their types (easy correction first, then
// misspelled) and to the length of the text that they cover (shorter first).
// The suggestions are sorted according to their types (easy correction first,
// misspelled second, then grammar error) and to the length of the text that they cover
// (shorter first).
Arrays.sort(suggestionSpans, mSuggestionSpanComparator);
mSpansLengths.clear();
@@ -3661,7 +3676,7 @@ public class Editor {
final int spanEnd = spannable.getSpanEnd(suggestionSpan);
if (misspelledSpanInfo != null
&& (suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
&& (suggestionSpan.getFlags() & FLAG_MISSPELLED_OR_GRAMMAR_ERROR) != 0) {
misspelledSpanInfo.mSuggestionSpan = suggestionSpan;
misspelledSpanInfo.mSpanStart = spanStart;
misspelledSpanInfo.mSpanEnd = spanEnd;

View File

@@ -338,11 +338,13 @@ public class SpellChecker implements SpellCheckerSessionListener {
((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
final boolean looksLikeTypo =
((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
final boolean looksLikeGrammarError =
((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR) > 0);
final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[k];
//TODO: we need to change that rule for results from a sentence-level spell
// checker that will probably be in dictionary.
if (!isInDictionary && looksLikeTypo) {
if (!isInDictionary && (looksLikeTypo || looksLikeGrammarError)) {
createMisspelledSuggestionSpan(
editable, suggestionsInfo, spellCheckSpan, offset, length);
} else {
@@ -482,8 +484,16 @@ public class SpellChecker implements SpellCheckerSessionListener {
suggestions = ArrayUtils.emptyArray(String.class);
}
SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions,
SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
final int suggestionsAttrs = suggestionsInfo.getSuggestionsAttributes();
int flags = SuggestionSpan.FLAG_EASY_CORRECT;
if ((suggestionsAttrs & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) != 0) {
flags |= SuggestionSpan.FLAG_MISSPELLED;
}
if ((suggestionsAttrs & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR) != 0) {
flags |= SuggestionSpan.FLAG_GRAMMAR_ERROR;
}
SuggestionSpan suggestionSpan =
new SuggestionSpan(mTextView.getContext(), suggestions, flags);
// TODO: Remove mIsSentenceSpellCheckSupported by extracting an interface
// to share the logic of word level spell checker and sentence level spell checker
if (mIsSentenceSpellCheckSupported) {

View File

@@ -199,6 +199,9 @@
<!-- The underline color and thickness for auto correction suggestion -->
<attr name="textAppearanceAutoCorrectionSuggestion" format="reference" />
<!-- The underline color and thickness for grammar error suggestion -->
<attr name="textAppearanceGrammarErrorSuggestion" format="reference" />
<!-- The underline color -->
<attr name="textUnderlineColor" format="reference|color" />
<!-- The underline thickness -->

View File

@@ -306,6 +306,10 @@ please see styles_device_defaults.xml.
<item name="textUnderlineColor">@color/holo_blue_light</item>
</style>
<style name="TextAppearance.GrammarErrorSuggestion" parent="TextAppearance.Suggestion">
<item name="textUnderlineColor">@color/holo_blue_light</item>
</style>
<!-- Widget Styles -->
<style name="Widget">

View File

@@ -263,6 +263,7 @@
<java-symbol type="attr" name="searchDialogTheme" />
<java-symbol type="attr" name="textAppearanceAutoCorrectionSuggestion" />
<java-symbol type="attr" name="textAppearanceEasyCorrectSuggestion" />
<java-symbol type="attr" name="textAppearanceGrammarErrorSuggestion" />
<java-symbol type="attr" name="textAppearanceMisspelledSuggestion" />
<java-symbol type="attr" name="textColorSearchUrl" />
<java-symbol type="attr" name="timePickerStyle" />

View File

@@ -93,6 +93,7 @@ please see themes_device_defaults.xml.
<item name="textAppearanceEasyCorrectSuggestion">@style/TextAppearance.EasyCorrectSuggestion</item>
<item name="textAppearanceMisspelledSuggestion">@style/TextAppearance.MisspelledSuggestion</item>
<item name="textAppearanceAutoCorrectionSuggestion">@style/TextAppearance.AutoCorrectionSuggestion</item>
<item name="textAppearanceGrammarErrorSuggestion">@style/TextAppearance.GrammarErrorSuggestion</item>
<item name="textAppearanceButton">@style/TextAppearance.Widget.Button</item>

View File

@@ -49010,6 +49010,7 @@ package android.text.style {
field @NonNull public static final android.os.Parcelable.Creator<android.text.style.SuggestionSpan> CREATOR;
field public static final int FLAG_AUTO_CORRECTION = 4; // 0x4
field public static final int FLAG_EASY_CORRECT = 1; // 0x1
field public static final int FLAG_GRAMMAR_ERROR = 8; // 0x8
field public static final int FLAG_MISSPELLED = 2; // 0x2
field public static final int SUGGESTIONS_MAX_SIZE = 5; // 0x5
field @Deprecated public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
@@ -56377,6 +56378,7 @@ package android.view.textservice {
field @NonNull public static final android.os.Parcelable.Creator<android.view.textservice.SuggestionsInfo> CREATOR;
field public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 4; // 0x4
field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
field public static final int RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR = 8; // 0x8
field public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 2; // 0x2
}