Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 490fa47a authored by Yohei Yukaw's avatar Yohei Yukaw
Browse files

Always specify non-null Locale object to SuggestionSpan

Confusingly, specifying a null Locale object to the constructor
of SuggestionSpan does not necessarily mean that
SuggestionSpan#getLocale() returns null.  The constructor in
question also receives Context object, and Context's locale can
be used as a fallback locale to initialize locale of
SuggestionSpan.

With this CL, LatinIME always specify non-null Locale object
when instantiating SuggestionSpan object.  It basically
corresponds to the active main dictionary, but can be
Locale#ROOT when one locale is not determined for some reasons.

BUG: 20435013
Change-Id: I2c152466410327300e7dba4d7ed9a22f57c17c4f
parent 54e891e2
Loading
Loading
Loading
Loading
+5 −6
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Locale;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public final class SuggestionSpanUtils {
@@ -57,13 +58,12 @@ public final class SuggestionSpanUtils {

    @UsedForTesting
    public static CharSequence getTextWithAutoCorrectionIndicatorUnderline(
            final Context context, final String text) {
            final Context context, final String text, @Nonnull final Locale locale) {
        if (TextUtils.isEmpty(text) || OBJ_FLAG_AUTO_CORRECTION == null) {
            return text;
        }
        final Spannable spannable = new SpannableString(text);
        // TODO: Set locale if it is feasible.
        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */,
        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, locale,
                new String[] {} /* suggestions */, OBJ_FLAG_AUTO_CORRECTION, null);
        spannable.setSpan(suggestionSpan, 0, text.length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
@@ -72,7 +72,7 @@ public final class SuggestionSpanUtils {

    @UsedForTesting
    public static CharSequence getTextWithSuggestionSpan(final Context context,
            final String pickedWord, final SuggestedWords suggestedWords) {
            final String pickedWord, final SuggestedWords suggestedWords, final Locale locale) {
        if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty()
                || suggestedWords.isPrediction() || suggestedWords.isPunctuationSuggestions()) {
            return pickedWord;
@@ -92,8 +92,7 @@ public final class SuggestionSpanUtils {
                suggestionsList.add(word.toString());
            }
        }
        // TODO: Set locale if it is feasible.
        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */,
        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, locale,
                suggestionsList.toArray(new String[suggestionsList.size()]), 0 /* flags */, null);
        final Spannable spannable = new SpannableString(pickedWord);
        spannable.setSpan(suggestionSpan, 0, pickedWord.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+16 −2
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import com.android.inputmethod.latin.utils.StatsUtils;
import com.android.inputmethod.latin.utils.TextRange;

import java.util.ArrayList;
import java.util.Locale;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

@@ -1902,6 +1903,15 @@ public final class InputLogic {
                SuggestedWords.NOT_A_SEQUENCE_NUMBER);
    }

    /**
     * @return the {@link Locale} of the {@link #mDictionaryFacilitator} if available. Otherwise
     * {@link Locale#ROOT}.
     */
    @Nonnull
    private Locale getDictionaryFacilitatorLocale() {
        return mDictionaryFacilitator != null ? mDictionaryFacilitator.getLocale() : Locale.ROOT;
    }

    /**
     * Gets a chunk of text with or the auto-correction indicator underline span as appropriate.
     *
@@ -1921,8 +1931,10 @@ public final class InputLogic {
     */
    // TODO: Shouldn't this go in some *Utils class instead?
    private CharSequence getTextWithUnderline(final String text) {
        // TODO: Locale should be determined based on context and the text given.
        return mIsAutoCorrectionIndicatorOn
                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(mLatinIME, text)
                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
                        mLatinIME, text, getDictionaryFacilitatorLocale())
                : text;
    }

@@ -2122,9 +2134,11 @@ public final class InputLogic {
            Log.d(TAG, "commitChosenWord() : [" + chosenWord + "]");
        }
        final SuggestedWords suggestedWords = mSuggestedWords;
        // TODO: Locale should be determined based on context and the text given.
        final Locale locale = getDictionaryFacilitatorLocale();
        final CharSequence chosenWordWithSuggestions =
                SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord,
                        suggestedWords);
                        suggestedWords, locale);
        if (DebugFlags.DEBUG_ENABLED) {
            long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
            Log.d(TAG, "commitChosenWord() : " + runTimeMillis + " ms to run "
+39 −5
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;

import javax.annotation.Nullable;

@SmallTest
public class SuggestionSpanUtilsTest extends AndroidTestCase {

@@ -62,7 +64,7 @@ public class SuggestionSpanUtilsTest extends AndroidTestCase {

    private static void assertSuggestionSpan(final String expectedText,
            final int reuiredSuggestionSpanFlags, final int requiredSpanFlags,
            final String[] expectedSuggestions,
            final String[] expectedSuggestions, @Nullable final Locale expectedLocale,
            final CharSequence actualText) {
        assertTrue(TextUtils.equals(expectedText, actualText));
        assertTrue(actualText instanceof Spanned);
@@ -84,22 +86,39 @@ public class SuggestionSpanUtilsTest extends AndroidTestCase {
                assertEquals(expectedSuggestions[i], actualSuggestions[i]);
            }
        }
        // CAVEAT: SuggestionSpan#getLocale() returns String rather than Locale object.
        assertEquals(expectedLocale.toString(), suggestionSpan.getLocale());
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
    public void testGetTextWithAutoCorrectionIndicatorUnderline() {
        final String ORIGINAL_TEXT = "Hey!";
        final Locale NONNULL_LOCALE = new Locale("en", "GB");
        final CharSequence text = SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
                getContext(), ORIGINAL_TEXT);
                getContext(), ORIGINAL_TEXT, NONNULL_LOCALE);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            assertNotSuggestionSpan(ORIGINAL_TEXT, text);
            return;
        }
        assertSuggestionSpan(ORIGINAL_TEXT,
                SuggestionSpan.FLAG_AUTO_CORRECTION /* reuiredSuggestionSpanFlags */,
                Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */,
                new String[]{}, NONNULL_LOCALE, text);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
    public void testGetTextWithAutoCorrectionIndicatorUnderlineRootLocale() {
        final String ORIGINAL_TEXT = "Hey!";
        final CharSequence text = SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
                getContext(), ORIGINAL_TEXT, Locale.ROOT);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            assertNotSuggestionSpan(ORIGINAL_TEXT, text);
            return;
        }
        assertSuggestionSpan(ORIGINAL_TEXT,
                SuggestionSpan.FLAG_AUTO_CORRECTION /* reuiredSuggestionSpanFlags */,
                Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */,
                new String[]{}, text);
                new String[]{}, Locale.ROOT, text);
    }

    public void testGetTextWithSuggestionSpan() {
@@ -119,6 +138,8 @@ public class SuggestionSpanUtilsTest extends AndroidTestCase {
            corrections[i] = createWordInfo("correction" + i, SuggestedWordInfo.KIND_CORRECTION);
        }

        final Locale NONNULL_LOCALE = new Locale("en", "GB");

        // SuggestionSpan will not be attached when {@link SuggestedWords#INPUT_STYLE_PREDICTION}
        // is specified.
        {
@@ -132,10 +153,11 @@ public class SuggestionSpanUtilsTest extends AndroidTestCase {
                    SuggestedWords.INPUT_STYLE_PREDICTION,
                    SuggestedWords.NOT_A_SEQUENCE_NUMBER);
            final String PICKED_WORD = prediction2.mWord;
            // Note that the framework uses the context locale as a fallback locale.
            assertNotSuggestionSpan(
                    PICKED_WORD,
                    SuggestionSpanUtils.getTextWithSuggestionSpan(getContext(), PICKED_WORD,
                            predictedWords));
                            predictedWords, NONNULL_LOCALE));
        }

        final ArrayList<SuggestedWordInfo> suggestedWordList = new ArrayList<>();
@@ -174,13 +196,25 @@ public class SuggestionSpanUtilsTest extends AndroidTestCase {
                expectedSuggestions.add(suggestedWord);
            }

            // non-null locale
            assertSuggestionSpan(
                    PICKED_WORD,
                    0 /* reuiredSuggestionSpanFlags */,
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */,
                    expectedSuggestions.toArray(new String[expectedSuggestions.size()]),
                    NONNULL_LOCALE,
                    SuggestionSpanUtils.getTextWithSuggestionSpan(getContext(), PICKED_WORD,
                            typedAndCollectedWords, NONNULL_LOCALE));

            // root locale
            assertSuggestionSpan(
                    PICKED_WORD,
                    0 /* reuiredSuggestionSpanFlags */,
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */,
                    expectedSuggestions.toArray(new String[expectedSuggestions.size()]),
                    Locale.ROOT,
                    SuggestionSpanUtils.getTextWithSuggestionSpan(getContext(), PICKED_WORD,
                            typedAndCollectedWords));
                            typedAndCollectedWords, Locale.ROOT));
        }
    }