diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 68a6b3e7823e1..a52d48e630d1a 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -723,7 +723,7 @@ class TextLine { float ret = 0; int contextLen = contextEnd - contextStart; - if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineCount != 0 || runIsRtl))) { + if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; if (mCharsValid) { ret = wp.getTextRunAdvances(mChars, start, runLen, @@ -753,7 +753,7 @@ class TextLine { wp.setColor(previousColor); } - if (wp.underlineCount != 0) { + if (wp.underlineColor != 0) { // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); @@ -764,11 +764,8 @@ class TextLine { wp.setStyle(Paint.Style.FILL); wp.setAntiAlias(true); - for (int i = 0; i < wp.underlineCount; i++) { - wp.setColor(wp.underlineColors[i]); - c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThicknesses[i], - wp); - } + wp.setColor(wp.underlineColor); + c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp); wp.setStyle(previousStyle); wp.setColor(previousColor); diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java index afd989256b88c..0447117119310 100644 --- a/core/java/android/text/TextPaint.java +++ b/core/java/android/text/TextPaint.java @@ -24,8 +24,6 @@ import android.graphics.Paint; */ public class TextPaint extends Paint { - private static final int DEFAULT_UNDERLINE_SIZE = 3; - // Special value 0 means no background paint public int bgColor; public int baselineShift; @@ -36,17 +34,12 @@ public class TextPaint extends Paint { * Special value 0 means no custom underline * @hide */ - public int[] underlineColors; + public int underlineColor = 0; /** * Defined as a multiplier of the default underline thickness. Use 1.0f for default thickness. * @hide */ - public float[] underlineThicknesses; - /** - * The number of underlines currently stored in the array. If 0, no underline is drawn. - * @hide - */ - public int underlineCount; + public float underlineThickness; public TextPaint() { super(); @@ -72,16 +65,8 @@ public class TextPaint extends Paint { linkColor = tp.linkColor; drawableState = tp.drawableState; density = tp.density; - - if (tp.underlineColors != null) { - if (underlineColors == null || underlineColors.length < tp.underlineCount) { - underlineColors = new int[tp.underlineCount]; - underlineThicknesses = new float[tp.underlineCount]; - } - System.arraycopy(tp.underlineColors, 0, underlineColors, 0, tp.underlineCount); - System.arraycopy(tp.underlineThicknesses, 0, underlineThicknesses, 0, tp.underlineCount); - } - underlineCount = tp.underlineCount; + underlineColor = tp.underlineColor; + underlineThickness = tp.underlineThickness; } /** @@ -91,31 +76,7 @@ public class TextPaint extends Paint { * @hide */ public void setUnderlineText(int color, float thickness) { - if (color == 0) { - // No underline - return; - } - - if (underlineCount == 0) { - underlineColors = new int[DEFAULT_UNDERLINE_SIZE]; - underlineThicknesses = new float[DEFAULT_UNDERLINE_SIZE]; - underlineColors[underlineCount] = color; - underlineThicknesses[underlineCount] = thickness; - underlineCount++; - } else { - if (underlineCount == underlineColors.length) { - int[] newColors = new int[underlineColors.length + DEFAULT_UNDERLINE_SIZE]; - float[] newThickness = new float[underlineThicknesses.length - + DEFAULT_UNDERLINE_SIZE]; - System.arraycopy(underlineColors, 0, newColors, 0, underlineColors.length); - System.arraycopy( - underlineThicknesses, 0, newThickness, 0, underlineThicknesses.length); - underlineColors = newColors; - underlineThicknesses = newThickness; - } - underlineColors[underlineCount] = color; - underlineThicknesses[underlineCount] = thickness; - underlineCount++; - } + underlineColor = color; + underlineThickness = thickness; } } diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index 51e9d7dd69592..1379dd2d8acc2 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -108,7 +108,8 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { /** * @param context Context for the application * @param locale locale Locale of the suggestions - * @param suggestions Suggestions for the string under the span + * @param suggestions Suggestions for the string under the span. Only the first up to + * {@link SuggestionSpan#SUGGESTIONS_MAX_SIZE} will be considered. * @param flags Additional flags indicating how this span is handled in TextView * @param notificationTargetClass if not null, this class will get notified when the user * selects one of the suggestions. @@ -258,10 +259,16 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { @Override public void updateDrawState(TextPaint tp) { - if ((mFlags & FLAG_MISSPELLED) != 0) { - tp.setUnderlineText(mMisspelledUnderlineColor, mMisspelledUnderlineThickness); - } else if ((mFlags & FLAG_EASY_CORRECT) != 0) { - tp.setUnderlineText(mEasyCorrectUnderlineColor, mEasyCorrectUnderlineThickness); + final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0; + final boolean easy = (mFlags & FLAG_EASY_CORRECT) != 0; + if (easy) { + if (!misspelled) { + 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); + } } } @@ -272,8 +279,15 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { */ public int getUnderlineColor() { // The order here should match what is used in updateDrawState - if ((mFlags & FLAG_MISSPELLED) != 0) return mMisspelledUnderlineColor; - if ((mFlags & FLAG_EASY_CORRECT) != 0) return mEasyCorrectUnderlineColor; + final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0; + final boolean easy = (mFlags & FLAG_EASY_CORRECT) != 0; + if (easy) { + if (!misspelled) { + return mEasyCorrectUnderlineColor; + } else { + return mMisspelledUnderlineColor; + } + } return 0; } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 70d2bd7cbe87c..ecd99b2c2ba4b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9560,6 +9560,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mNumberOfSuggestions; private boolean mCursorWasVisibleBeforeSuggestions; private SuggestionAdapter mSuggestionsAdapter; + private final Comparator mSuggestionSpanComparator; + private final HashMap mSpansLengths; + private class CustomPopupWindow extends PopupWindow { public CustomPopupWindow(Context context, int defStyle) { @@ -9585,6 +9588,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public SuggestionsPopupWindow() { mCursorWasVisibleBeforeSuggestions = mCursorVisible; + mSuggestionSpanComparator = new SuggestionSpanComparator(); + mSpansLengths = new HashMap(); } @Override @@ -9663,6 +9668,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + private class SuggestionSpanComparator implements Comparator { + public int compare(SuggestionSpan span1, SuggestionSpan span2) { + 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; + } + + return mSpansLengths.get(span1).intValue() - mSpansLengths.get(span2).intValue(); + } + } + /** * Returns the suggestion spans that cover the current cursor position. The suggestion * spans are sorted according to the length of text that they are attached to. @@ -9672,24 +9697,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Spannable spannable = (Spannable) TextView.this.mText; SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class); - // Cache the span length for performance reason. - final HashMap spansLengths = - new HashMap(); - + mSpansLengths.clear(); for (SuggestionSpan suggestionSpan : suggestionSpans) { int start = spannable.getSpanStart(suggestionSpan); int end = spannable.getSpanEnd(suggestionSpan); - spansLengths.put(suggestionSpan, Integer.valueOf(end - start)); + mSpansLengths.put(suggestionSpan, Integer.valueOf(end - start)); } - // The suggestions are sorted according to the lenght of the text that they cover - // (shorter first) - Arrays.sort(suggestionSpans, new Comparator() { - public int compare(SuggestionSpan span1, SuggestionSpan span2) { - return spansLengths.get(span1).intValue() - spansLengths.get(span2).intValue(); - } - }); - + // 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). + Arrays.sort(suggestionSpans, mSuggestionSpanComparator); return suggestionSpans; }