From c9fd978da60f76c0576150c55629a034e1fa19fb Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Fri, 9 Sep 2011 09:20:12 -0700 Subject: [PATCH] Bug 5281947: add to dictionnary option promoted in suggestions. When several SuggestionSpans are available at a given position, their content are merged, in creation time order. As a result, the IME's suggestions are picked before the spell check, and no add to dictionnary option is created. This CL modifies the comparator to make easy correction spans appear first (Voice IME), then misspelled words and then regular suggestions. Also avoids the creation of a new comparator and length hash map on every display. Change-Id: I1f9f031a6fdcbbc09f248a192b83051092765f8e --- core/java/android/text/TextLine.java | 11 ++-- core/java/android/text/TextPaint.java | 51 +++---------------- .../android/text/style/SuggestionSpan.java | 28 +++++++--- core/java/android/widget/TextView.java | 43 +++++++++++----- 4 files changed, 61 insertions(+), 72 deletions(-) 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 b9481140c7213..7cceb95a8583b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9547,6 +9547,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) { @@ -9572,6 +9575,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public SuggestionsPopupWindow() { mCursorWasVisibleBeforeSuggestions = mCursorVisible; + mSuggestionSpanComparator = new SuggestionSpanComparator(); + mSpansLengths = new HashMap(); } @Override @@ -9650,6 +9655,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. @@ -9659,24 +9684,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; }