chromium/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionsPopupWindow.java

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.content.browser.input;

import android.content.Context;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.TextAppearanceSpan;
import android.view.View;

import org.chromium.content.R;
import org.chromium.ui.base.WindowAndroid;

/**
 * A subclass of SuggestionsPopupWindow to be used for showing suggestions from one or more
 * SuggestionSpans.
 */
public class TextSuggestionsPopupWindow extends SuggestionsPopupWindow {
    private SuggestionInfo[] mSuggestionInfos;
    private TextAppearanceSpan mPrefixSpan;
    private TextAppearanceSpan mSuffixSpan;

    /**
     * @param context Android context to use.
     * @param textSuggestionHost TextSuggestionHost instance (used to communicate with Blink).
     * @param windowAndroid The current WindowAndroid instance.
     * @param parentView The view used to attach the PopupWindow.
     */
    public TextSuggestionsPopupWindow(
            Context context,
            TextSuggestionHost textSuggestionHost,
            WindowAndroid windowAndroid,
            View parentView) {
        super(context, textSuggestionHost, windowAndroid, parentView);

        mPrefixSpan =
                new TextAppearanceSpan(context, R.style.TextAppearance_SuggestionPrefixOrSuffix);
        mSuffixSpan =
                new TextAppearanceSpan(context, R.style.TextAppearance_SuggestionPrefixOrSuffix);
    }

    /** Shows the text suggestion menu at the specified coordinates (relative to the viewport). */
    public void show(
            double caretX,
            double caretY,
            String highlightedText,
            SuggestionInfo[] suggestionInfos) {
        mSuggestionInfos = suggestionInfos.clone();

        // Android's Editor.java shows the "Add to dictonary" button if and only if there's a
        // SuggestionSpan with FLAG_MISSPELLED set. However, some OEMs (e.g. Samsung) appear to
        // change the behavior on their devices to never show this button, since their IMEs don't go
        // through the normal spell-checking API and instead add SuggestionSpans directly. Since
        // it's difficult to determine how the OEM has set up the native menu, we instead only show
        // the "Add to dictionary" button for spelling markers added by Chrome from running the
        // system spell checker. SuggestionSpans with FLAG_MISSPELLED set (i.e., a spelling
        // underline added directly by the IME) do not show this button.
        setAddToDictionaryEnabled(false);
        super.show(caretX, caretY, highlightedText);
    }

    @Override
    protected int getSuggestionsCount() {
        return mSuggestionInfos.length;
    }

    @Override
    protected Object getSuggestionItem(int position) {
        return mSuggestionInfos[position];
    }

    @Override
    protected SpannableString getSuggestionText(int position) {
        final SuggestionInfo suggestionInfo = mSuggestionInfos[position];

        SpannableString suggestionText =
                new SpannableString(
                        suggestionInfo.getPrefix()
                                + suggestionInfo.getSuggestion()
                                + suggestionInfo.getSuffix());

        // Gray out prefix text (if any).
        suggestionText.setSpan(
                mPrefixSpan,
                0,
                suggestionInfo.getPrefix().length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        // Gray out suffix text (if any).
        suggestionText.setSpan(
                mSuffixSpan,
                suggestionInfo.getPrefix().length() + suggestionInfo.getSuggestion().length(),
                suggestionInfo.getPrefix().length()
                        + suggestionInfo.getSuggestion().length()
                        + suggestionInfo.getSuffix().length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        return suggestionText;
    }

    @Override
    protected void applySuggestion(int position) {
        SuggestionInfo info = mSuggestionInfos[position];
        mTextSuggestionHost.applyTextSuggestion(info.getMarkerTag(), info.getSuggestionIndex());
    }
}