chromium/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.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.chrome.browser.infobar;

import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;

import com.google.android.material.tabs.TabLayout;

import org.jni_zero.CalledByNative;
import org.jni_zero.NativeMethods;

import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.preferences.PrefChangeRegistrar;
import org.chromium.chrome.browser.preferences.PrefChangeRegistrar.PrefObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManagerProvider;
import org.chromium.components.browser_ui.styles.SemanticColorUtils;
import org.chromium.components.infobars.InfoBar;
import org.chromium.components.infobars.InfoBarCompactLayout;
import org.chromium.components.translate.TranslateFeatureMap;
import org.chromium.components.translate.TranslateMenu;
import org.chromium.components.translate.TranslateMenuHelper;
import org.chromium.components.translate.TranslateOption;
import org.chromium.components.translate.TranslateOptions;
import org.chromium.components.translate.TranslateTabLayout;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.widget.Toast;

/** Java version of the compact translate infobar. */
public class TranslateCompactInfoBar extends InfoBar
        implements TabLayout.OnTabSelectedListener,
                TranslateMenuHelper.TranslateMenuListener,
                PrefObserver {
    public static final int TRANSLATING_INFOBAR = 1;
    public static final int AFTER_TRANSLATING_INFOBAR = 2;

    private static final int SOURCE_TAB_INDEX = 0;
    private static final int TARGET_TAB_INDEX = 1;

    // Action ID for Snackbar.
    // Actions performed by clicking on on the overflow menu.
    public static final int ACTION_OVERFLOW_ALWAYS_TRANSLATE = 0;
    public static final int ACTION_OVERFLOW_NEVER_SITE = 1;
    public static final int ACTION_OVERFLOW_NEVER_LANGUAGE = 2;
    // Actions triggered automatically. (when translation or denied count reaches the threshold.)
    public static final int ACTION_AUTO_ALWAYS_TRANSLATE = 3;
    public static final int ACTION_AUTO_NEVER_LANGUAGE = 4;

    private final int mInitialStep;
    private final int mDefaultTextColor;
    private final TranslateOptions mOptions;

    private long mNativeTranslateInfoBarPtr;
    private TranslateTabLayout mTabLayout;

    private static final String INFOBAR_HISTOGRAM = "Translate.CompactInfobar.Event";

    // Need 2 instances of TranslateMenuHelper to prevent a race condition bug which happens when
    // showing language menu after dismissing overflow menu.
    private TranslateMenuHelper mOverflowMenuHelper;
    private TranslateMenuHelper mLanguageMenuHelper;

    private ImageButton mMenuButton;
    private InfoBarCompactLayout mParent;

    private final WindowAndroid mWindowAndroid;
    private TranslateSnackbarController mSnackbarController;

    private boolean mMenuExpanded;
    private boolean mIsFirstLayout = true;
    private boolean mUserInteracted;

    private final PrefChangeRegistrar mPrefChangeRegistrar;

    /**
     * The controller for translate UI snackbars. The snackbars show when a translate preference has
     * been enabled. The |onAction| method cancels the action, while onDismissNoAction toggles the
     * preference.
     */
    class TranslateSnackbarController implements SnackbarController {
        private final int mActionId;

        /** @param actionId Overflow menu action id for translate preference to enable.*/
        public TranslateSnackbarController(int actionId) {
            mActionId = actionId;
        }

        @Override
        public void onDismissNoAction(Object actionData) {
            mSnackbarController = null;
            handleTranslateOverflowOption(mActionId);
        }

        @Override
        public void onAction(Object actionData) {
            mSnackbarController = null;
            switch (mActionId) {
                case ACTION_OVERFLOW_ALWAYS_TRANSLATE:
                    recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_CANCEL_ALWAYS);
                    return;
                case ACTION_AUTO_ALWAYS_TRANSLATE:
                    recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_CANCEL_AUTO_ALWAYS);
                    return;
                case ACTION_OVERFLOW_NEVER_LANGUAGE:
                    recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_CANCEL_NEVER);
                    return;
                case ACTION_AUTO_NEVER_LANGUAGE:
                    recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_CANCEL_AUTO_NEVER);
                    // This snackbar is triggered automatically after a close button click.  Need to
                    // dismiss the infobar even if the user cancels the "Never Translate".
                    closeInfobar(false);
                    return;
                case ACTION_OVERFLOW_NEVER_SITE:
                    recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_CANCEL_NEVER_SITE);
                    return;
                default:
                    assert false : "Unsupported Menu Item Id, when handling snackbar action";
                    return;
            }
        }
    }

    @CalledByNative
    private static InfoBar create(
            Tab tab,
            int initialStep,
            String sourceLanguageCode,
            String targetLanguageCode,
            boolean neverLanguage /* Never automatically translate the source language */,
            boolean neverDomain /* Never automatically translate the domain */,
            boolean alwaysTranslate /* Always automatically translate the source language */,
            boolean triggeredFromMenu,
            String[] allLanguages,
            String[] allLanguagesCodes,
            int[] allLanguagesHashCodes,
            String[] contentLanguagesCodes,
            int tabTextColor) {
        recordInfobarAction(InfobarEvent.INFOBAR_IMPRESSION);

        return new TranslateCompactInfoBar(
                tab.getWindowAndroid(),
                initialStep,
                sourceLanguageCode,
                targetLanguageCode,
                neverLanguage,
                neverDomain,
                alwaysTranslate,
                triggeredFromMenu,
                allLanguages,
                allLanguagesCodes,
                allLanguagesHashCodes,
                contentLanguagesCodes,
                tabTextColor);
    }

    TranslateCompactInfoBar(
            WindowAndroid windowAndroid,
            int initialStep,
            String sourceLanguageCode,
            String targetLanguageCode,
            boolean neverLanguage,
            boolean neverDomain,
            boolean alwaysTranslate,
            boolean triggeredFromMenu,
            String[] allLanguages,
            String[] allLanguagesCodes,
            int[] allLanguagesHashCodes,
            String[] contentLanguagesCodes,
            int tabTextColor) {
        super(R.drawable.infobar_translate_compact, 0, null, null);
        mWindowAndroid = windowAndroid;

        if (TranslateFeatureMap.isEnabled(TranslateFeatureMap.CONTENT_LANGUAGES_IN_LANGUAGE_PICKER)
                && !TranslateFeatureMap.getInstance()
                        .getFieldTrialParamByFeatureAsBoolean(
                                TranslateFeatureMap.CONTENT_LANGUAGES_IN_LANGUAGE_PICKER,
                                TranslateFeatureMap.CONTENT_LANGUAGES_DISABLE_OBSERVERS_PARAM,
                                false)) {
            mPrefChangeRegistrar = new PrefChangeRegistrar();
            mPrefChangeRegistrar.addObserver(Pref.ACCEPT_LANGUAGES, this);
        } else {
            mPrefChangeRegistrar = null;
        }
        mInitialStep = initialStep;
        mDefaultTextColor = tabTextColor;
        mOptions =
                TranslateOptions.create(
                        sourceLanguageCode,
                        targetLanguageCode,
                        allLanguages,
                        allLanguagesCodes,
                        neverLanguage,
                        neverDomain,
                        alwaysTranslate,
                        triggeredFromMenu,
                        allLanguagesHashCodes,
                        contentLanguagesCodes);
    }

    @Override
    public void onPreferenceChange() {
        if (mNativeTranslateInfoBarPtr != 0) {
            String[] currentContentLanguages =
                    TranslateCompactInfoBarJni.get()
                            .getContentLanguagesCodes(
                                    mNativeTranslateInfoBarPtr, TranslateCompactInfoBar.this);
            mOptions.updateContentLanguages(currentContentLanguages);
            if (mLanguageMenuHelper != null) {
                mLanguageMenuHelper.onContentLanguagesChanged(currentContentLanguages);
            }
        }
    }

    @Override
    protected boolean usesCompactLayout() {
        return true;
    }

    @Override
    protected void createCompactLayoutContent(InfoBarCompactLayout parent) {
        LinearLayout content =
                (LinearLayout)
                        LayoutInflater.from(getContext())
                                .inflate(R.layout.infobar_translate_compact_content, parent, false);

        // When parent tab is being switched out (view detached), dismiss all menus and snackbars.
        content.addOnAttachStateChangeListener(
                new View.OnAttachStateChangeListener() {
                    @Override
                    public void onViewAttachedToWindow(View view) {}

                    @Override
                    public void onViewDetachedFromWindow(View view) {
                        dismissMenusAndSnackbars();
                    }
                });

        mTabLayout = content.findViewById(R.id.translate_infobar_tabs);
        if (mDefaultTextColor > 0) {
            mTabLayout.setTabTextColors(
                    SemanticColorUtils.getDefaultTextColor(getContext()),
                    SemanticColorUtils.getDefaultTextColorAccent1(getContext())
                    /* tab_layout_selected_tab_color= */ );
        }
        mTabLayout.addTabs(mOptions.sourceLanguageName(), mOptions.targetLanguageName());

        if (mInitialStep == TRANSLATING_INFOBAR) {
            // Set translating status in the beginning for pages translated automatically.
            mTabLayout.getTabAt(TARGET_TAB_INDEX).select();
            mTabLayout.showProgressBarOnTab(TARGET_TAB_INDEX);
            if (mOptions.triggeredFromMenu()) {
                mUserInteracted = true;
            }
        } else if (mInitialStep == AFTER_TRANSLATING_INFOBAR) {
            // Focus on target tab since we are after translation.
            mTabLayout.getTabAt(TARGET_TAB_INDEX).select();
        }

        mTabLayout.addOnTabSelectedListener(this);

        // Dismiss all menus and end scrolling animation when there is layout changed.
        mTabLayout.addOnLayoutChangeListener(
                new OnLayoutChangeListener() {
                    @Override
                    public void onLayoutChange(
                            View v,
                            int left,
                            int top,
                            int right,
                            int bottom,
                            int oldLeft,
                            int oldTop,
                            int oldRight,
                            int oldBottom) {
                        if (left != oldLeft
                                || top != oldTop
                                || right != oldRight
                                || bottom != oldBottom) {
                            // Dismiss all menus to prevent menu misplacement.
                            dismissMenus();

                            if (mIsFirstLayout) {
                                // Scrolls to the end to make sure the target language tab is
                                // visible when language tabs is too long.
                                mTabLayout.startScrollingAnimationIfNeeded();
                                mIsFirstLayout = false;
                                return;
                            }

                            // End scrolling animation when layout changed.
                            mTabLayout.endScrollingAnimationIfPlaying();
                        }
                    }
                });

        mMenuButton = content.findViewById(R.id.translate_infobar_menu_button);
        mMenuButton.setOnClickListener(
                new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mTabLayout.endScrollingAnimationIfPlaying();
                        recordInfobarAction(InfobarEvent.INFOBAR_OPTIONS);
                        initMenuHelper(TranslateMenu.MENU_OVERFLOW);
                        mOverflowMenuHelper.show(TranslateMenu.MENU_OVERFLOW, getParentWidth());
                        mMenuExpanded = true;
                    }
                });

        parent.addContent(content, 1.0f);
        mParent = parent;
    }

    private void initMenuHelper(int menuType) {
        boolean isIncognito =
                TranslateCompactInfoBarJni.get()
                        .isIncognito(mNativeTranslateInfoBarPtr, TranslateCompactInfoBar.this);
        boolean isSourceLangUnknown = mOptions.sourceLanguageCode().equals("und");
        switch (menuType) {
            case TranslateMenu.MENU_OVERFLOW:
                mOverflowMenuHelper =
                        new TranslateMenuHelper(
                                getContext(),
                                mMenuButton,
                                mOptions,
                                this,
                                isIncognito,
                                isSourceLangUnknown);
                return;
            case TranslateMenu.MENU_TARGET_LANGUAGE:
            case TranslateMenu.MENU_SOURCE_LANGUAGE:
                if (mLanguageMenuHelper == null) {
                    mLanguageMenuHelper =
                            new TranslateMenuHelper(
                                    getContext(),
                                    mMenuButton,
                                    mOptions,
                                    this,
                                    isIncognito,
                                    isSourceLangUnknown);
                }
                return;
            default:
                assert false : "Unsupported Menu Item Id";
        }
    }

    /**
     * Selects the tab at tabIndex without triggering onTabSelected. This avoids treating the
     * selection as a user interaction and prevents a potential loop where a translate state change
     * updates the UI, which then updates the translate state.
     */
    private void silentlySelectTabAt(int tabIndex) {
        if (mTabLayout == null) {
            return;
        }

        mTabLayout.removeOnTabSelectedListener(this);
        mTabLayout.getTabAt(tabIndex).select();
        mTabLayout.addOnTabSelectedListener(this);
    }

    /** Begins the translation process and marks it as initiated by the user. */
    private void startUserInitiatedTranslation() {
        mUserInteracted = true;
        onButtonClicked(ActionType.TRANSLATE);
    }

    @CalledByNative
    private void onTranslating() {
        if (mTabLayout != null) {
            silentlySelectTabAt(TARGET_TAB_INDEX);
            mTabLayout.showProgressBarOnTab(TARGET_TAB_INDEX);
        }
    }

    @CalledByNative
    private boolean onPageTranslated(int errorType) {
        boolean errorUIShown = false;
        if (mTabLayout != null) {
            mTabLayout.hideProgressBar();
            if (errorType != 0) {
                Toast toast =
                        Toast.makeText(
                                getContext(), R.string.translate_infobar_error, Toast.LENGTH_SHORT);
                int[] location = new int[2];
                mTabLayout.getLocationOnScreen(location);
                int yOffset =
                        location[1]
                                - mTabLayout.getHeight()
                                - getContext()
                                        .getResources()
                                        .getDimensionPixelSize(R.dimen.translate_toast_y_offset);
                toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, yOffset);
                toast.show();
                errorUIShown = true;
                silentlySelectTabAt(SOURCE_TAB_INDEX);
            }
        }
        return errorUIShown;
    }

    @CalledByNative
    private void setNativePtr(long nativePtr) {
        mNativeTranslateInfoBarPtr = nativePtr;
    }

    @CalledByNative
    private void setAutoAlwaysTranslate() {
        createAndShowSnackbar(
                getContext()
                        .getString(
                                R.string.translate_snackbar_always_translate,
                                mOptions.sourceLanguageName(),
                                mOptions.targetLanguageName()),
                Snackbar.UMA_TRANSLATE_ALWAYS,
                ACTION_AUTO_ALWAYS_TRANSLATE);
    }

    private boolean updateTargetLanguage(String languageCode) {
        // Set the target code in TranslateOptions.
        if (!mOptions.setTargetLanguage(languageCode)) {
            return false;
        }

        // Adjust UI if options were updated successfully.
        mTabLayout.replaceTabTitle(
                TARGET_TAB_INDEX, mOptions.getRepresentationFromCode(languageCode));
        return true;
    }

    @CalledByNative
    public void onTargetLanguageChanged(String languageCode) {
        updateTargetLanguage(languageCode);
    }

    @Override
    protected void resetNativeInfoBar() {
        mNativeTranslateInfoBarPtr = 0;
        super.resetNativeInfoBar();
    }

    private void closeInfobar(boolean explicitly) {
        if (isDismissed()) return;

        if (!mUserInteracted) {
            recordInfobarAction(InfobarEvent.INFOBAR_DECLINE);
        }

        // Check if we should trigger the auto "never translate" if infobar is closed explicitly.
        if (explicitly
                && mNativeTranslateInfoBarPtr != 0
                && TranslateCompactInfoBarJni.get()
                        .shouldAutoNeverTranslate(
                                mNativeTranslateInfoBarPtr,
                                TranslateCompactInfoBar.this,
                                mMenuExpanded)) {
            createAndShowSnackbar(
                    getContext()
                            .getString(
                                    R.string.translate_snackbar_language_never,
                                    mOptions.sourceLanguageName()),
                    Snackbar.UMA_TRANSLATE_NEVER,
                    ACTION_AUTO_NEVER_LANGUAGE);
            // Postpone the infobar dismiss until the snackbar finished showing.  Otherwise, the
            // reference to the native infobar is killed and there is no way for the snackbar to
            // perform the action.
            return;
        }
        // This line will dismiss this infobar.
        super.onCloseButtonClicked();
    }

    @Override
    public void onCloseButtonClicked() {
        if (mPrefChangeRegistrar != null) {
            mPrefChangeRegistrar.destroy();
        }
        mTabLayout.endScrollingAnimationIfPlaying();
        closeInfobar(true);
    }

    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        switch (tab.getPosition()) {
            case SOURCE_TAB_INDEX:
                recordInfobarAction(InfobarEvent.INFOBAR_REVERT);
                mUserInteracted = true;
                onButtonClicked(ActionType.TRANSLATE_SHOW_ORIGINAL);
                return;
            case TARGET_TAB_INDEX:
                recordInfobarAction(InfobarEvent.INFOBAR_TARGET_TAB_TRANSLATE);
                startUserInitiatedTranslation();
                return;
            default:
                assert false : "Unexpected Tab Index";
        }
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {}

    @Override
    public void onTabReselected(TabLayout.Tab tab) {}

    @Override
    public void onOverflowMenuItemClicked(int itemId) {
        switch (itemId) {
            case TranslateMenu.ID_OVERFLOW_MORE_LANGUAGE:
                recordInfobarAction(InfobarEvent.INFOBAR_MORE_LANGUAGES);
                initMenuHelper(TranslateMenu.MENU_TARGET_LANGUAGE);
                mLanguageMenuHelper.show(TranslateMenu.MENU_TARGET_LANGUAGE, getParentWidth());
                return;
            case TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE:
                // Only show snackbar when "Always Translate" is enabled.
                if (!mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE)) {
                    recordInfobarAction(InfobarEvent.INFOBAR_ALWAYS_TRANSLATE);
                    createAndShowSnackbar(
                            getContext()
                                    .getString(
                                            R.string.translate_snackbar_always_translate,
                                            mOptions.sourceLanguageName(),
                                            mOptions.targetLanguageName()),
                            Snackbar.UMA_TRANSLATE_ALWAYS,
                            ACTION_OVERFLOW_ALWAYS_TRANSLATE);
                } else {
                    recordInfobarAction(InfobarEvent.INFOBAR_ALWAYS_TRANSLATE_UNDO);
                    handleTranslateOverflowOption(ACTION_OVERFLOW_ALWAYS_TRANSLATE);
                }
                return;
            case TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE:
                // Only show snackbar when "Never Translate" is enabled.
                if (!mOptions.getTranslateState(TranslateOptions.Type.NEVER_LANGUAGE)) {
                    recordInfobarAction(InfobarEvent.INFOBAR_NEVER_TRANSLATE);
                    createAndShowSnackbar(
                            getContext()
                                    .getString(
                                            R.string.translate_snackbar_language_never,
                                            mOptions.sourceLanguageName()),
                            Snackbar.UMA_TRANSLATE_NEVER,
                            ACTION_OVERFLOW_NEVER_LANGUAGE);
                } else {
                    recordInfobarAction(InfobarEvent.INFOBAR_NEVER_TRANSLATE_UNDO);
                    handleTranslateOverflowOption(ACTION_OVERFLOW_NEVER_LANGUAGE);
                }
                return;
            case TranslateMenu.ID_OVERFLOW_NEVER_SITE:
                // Only show snackbar when "Never Translate" is enabled.
                if (!mOptions.getTranslateState(TranslateOptions.Type.NEVER_DOMAIN)) {
                    recordInfobarAction(InfobarEvent.INFOBAR_NEVER_TRANSLATE_SITE);
                    createAndShowSnackbar(
                            getContext().getString(R.string.translate_snackbar_site_never),
                            Snackbar.UMA_TRANSLATE_NEVER_SITE,
                            ACTION_OVERFLOW_NEVER_SITE);
                } else {
                    recordInfobarAction(InfobarEvent.INFOBAR_NEVER_TRANSLATE_SITE_UNDO);
                    handleTranslateOverflowOption(ACTION_OVERFLOW_NEVER_SITE);
                }
                return;
            case TranslateMenu.ID_OVERFLOW_NOT_THIS_LANGUAGE:
                recordInfobarAction(InfobarEvent.INFOBAR_PAGE_NOT_IN);
                initMenuHelper(TranslateMenu.MENU_SOURCE_LANGUAGE);
                mLanguageMenuHelper.show(TranslateMenu.MENU_SOURCE_LANGUAGE, getParentWidth());
                return;
            default:
                assert false : "Unexpected overflow menu code";
        }
    }

    @Override
    public void onTargetMenuItemClicked(String languageCode) {
        // Set the target code in both UI and native.
        if (mNativeTranslateInfoBarPtr != 0 && updateTargetLanguage(languageCode)) {
            recordInfobarAction(InfobarEvent.INFOBAR_MORE_LANGUAGES_TRANSLATE);
            // Update the target language in the backend.
            TranslateCompactInfoBarJni.get()
                    .applyStringTranslateOption(
                            mNativeTranslateInfoBarPtr,
                            TranslateCompactInfoBar.this,
                            TranslateOption.TARGET_CODE,
                            languageCode);
            startUserInitiatedTranslation();
        }
    }

    @Override
    public void onSourceMenuItemClicked(String languageCode) {
        // Set the source code in both UI and native.
        if (mNativeTranslateInfoBarPtr != 0 && mOptions.setSourceLanguage(languageCode)) {
            TranslateCompactInfoBarJni.get()
                    .applyStringTranslateOption(
                            mNativeTranslateInfoBarPtr,
                            TranslateCompactInfoBar.this,
                            TranslateOption.SOURCE_CODE,
                            languageCode);
            // Adjust UI.
            mTabLayout.replaceTabTitle(
                    SOURCE_TAB_INDEX, mOptions.getRepresentationFromCode(languageCode));
            startUserInitiatedTranslation();
        }
    }

    // Dismiss all overflow menus that remains open.
    // This is called when infobar started hiding or layout changed.
    private void dismissMenus() {
        if (mOverflowMenuHelper != null) mOverflowMenuHelper.dismiss();
        if (mLanguageMenuHelper != null) mLanguageMenuHelper.dismiss();
    }

    // Dismiss all overflow menus and snackbars that belong to this infobar and remain open.
    private void dismissMenusAndSnackbars() {
        dismissMenus();
        if (getSnackbarManager() != null && mSnackbarController != null) {
            getSnackbarManager().dismissSnackbars(mSnackbarController);
        }
    }

    @Override
    protected void onStartedHiding() {
        dismissMenusAndSnackbars();
    }

    @Override
    protected CharSequence getAccessibilityMessage(CharSequence defaultMessage) {
        return getContext().getString(R.string.translate_button);
    }

    /** Returns true if overflow menu is showing. This is only used for automation testing. */
    public boolean isShowingOverflowMenuForTesting() {
        if (mOverflowMenuHelper == null) return false;
        return mOverflowMenuHelper.isShowing();
    }

    /** Returns true if language menu is showing. This is only used for automation testing. */
    public boolean isShowingLanguageMenuForTesting() {
        if (mLanguageMenuHelper == null) return false;
        return mLanguageMenuHelper.isShowing();
    }

    /**
     * Returns true if the tab at the given |tabIndex| is selected. This is only used for automation
     * testing.
     */
    private boolean isTabSelectedForTesting(int tabIndex) {
        return mTabLayout.getTabAt(tabIndex).isSelected();
    }

    /** Returns true if the target tab is selected. This is only used for automation testing. */
    public boolean isSourceTabSelectedForTesting() {
        return this.isTabSelectedForTesting(SOURCE_TAB_INDEX);
    }

    /** Returns true if the target tab is selected. This is only used for automation testing. */
    public boolean isTargetTabSelectedForTesting() {
        return this.isTabSelectedForTesting(TARGET_TAB_INDEX);
    }

    private void createAndShowSnackbar(String title, int umaType, int actionId) {
        if (getSnackbarManager() == null) {
            // Directly apply menu option, if snackbar system is not working.
            handleTranslateOverflowOption(actionId);
            return;
        }
        switch (actionId) {
            case ACTION_OVERFLOW_ALWAYS_TRANSLATE:
                recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_ALWAYS_TRANSLATE_IMPRESSION);
                break;
            case ACTION_AUTO_ALWAYS_TRANSLATE:
                recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION);
                break;
            case ACTION_OVERFLOW_NEVER_LANGUAGE:
                recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_NEVER_TRANSLATE_IMPRESSION);
                break;
            case ACTION_AUTO_NEVER_LANGUAGE:
                recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_AUTO_NEVER_IMPRESSION);
                break;
            case ACTION_OVERFLOW_NEVER_SITE:
                recordInfobarAction(InfobarEvent.INFOBAR_SNACKBAR_NEVER_TRANSLATE_SITE_IMPRESSION);
                break;
            default:
                assert false : "Unsupported Menu Item Id, to show snackbar.";
        }

        mSnackbarController = new TranslateSnackbarController(actionId);
        getSnackbarManager()
                .showSnackbar(
                        Snackbar.make(
                                        title,
                                        mSnackbarController,
                                        Snackbar.TYPE_NOTIFICATION,
                                        umaType)
                                .setSingleLine(false)
                                .setAction(
                                        getContext().getString(R.string.translate_snackbar_cancel),
                                        null));
    }

    private SnackbarManager getSnackbarManager() {
        return SnackbarManagerProvider.from(mWindowAndroid);
    }

    private void handleTranslateOverflowOption(int actionId) {
        // Quit if native is destroyed.
        if (mNativeTranslateInfoBarPtr == 0) return;

        switch (actionId) {
            case ACTION_OVERFLOW_ALWAYS_TRANSLATE:
                mUserInteracted = true;
                toggleAlwaysTranslate();
                // Start translating if always translate is selected and if page is not already
                // translated to the target language.
                if (mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE)
                        && mTabLayout.getSelectedTabPosition() == SOURCE_TAB_INDEX) {
                    startUserInitiatedTranslation();
                }
                return;
            case ACTION_AUTO_ALWAYS_TRANSLATE:
                toggleAlwaysTranslate();
                return;
            case ACTION_OVERFLOW_NEVER_LANGUAGE:
                mUserInteracted = true;
                // Fallthrough intentional.
            case ACTION_AUTO_NEVER_LANGUAGE:
                mOptions.toggleNeverTranslateLanguageState(
                        !mOptions.getTranslateState(TranslateOptions.Type.NEVER_LANGUAGE));
                // If toggling never translate to true, after applying this option the translation
                // will revert and the infobar will dismiss.
                TranslateCompactInfoBarJni.get()
                        .applyBoolTranslateOption(
                                mNativeTranslateInfoBarPtr,
                                TranslateCompactInfoBar.this,
                                TranslateOption.NEVER_TRANSLATE,
                                mOptions.getTranslateState(TranslateOptions.Type.NEVER_LANGUAGE));
                return;
            case ACTION_OVERFLOW_NEVER_SITE:
                mOptions.toggleNeverTranslateDomainState(
                        !mOptions.getTranslateState(TranslateOptions.Type.NEVER_DOMAIN));
                mUserInteracted = true;
                // If toggling never translate to true, after applying this option the translation
                // will revert and the infobar will dismiss.
                TranslateCompactInfoBarJni.get()
                        .applyBoolTranslateOption(
                                mNativeTranslateInfoBarPtr,
                                TranslateCompactInfoBar.this,
                                TranslateOption.NEVER_TRANSLATE_SITE,
                                mOptions.getTranslateState(TranslateOptions.Type.NEVER_DOMAIN));
                return;
            default:
                assert false : "Unsupported Menu Item Id, in handle post snackbar";
        }
    }

    private void toggleAlwaysTranslate() {
        mOptions.toggleAlwaysTranslateLanguageState(
                !mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE));
        TranslateCompactInfoBarJni.get()
                .applyBoolTranslateOption(
                        mNativeTranslateInfoBarPtr,
                        TranslateCompactInfoBar.this,
                        TranslateOption.ALWAYS_TRANSLATE,
                        mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE));
    }

    private static void recordInfobarAction(int action) {
        RecordHistogram.recordEnumeratedHistogram(
                INFOBAR_HISTOGRAM, action, InfobarEvent.INFOBAR_HISTOGRAM_BOUNDARY);
    }

    // Return the width of parent in pixels.  Return 0 if there is no parent.
    private int getParentWidth() {
        return mParent != null ? mParent.getWidth() : 0;
    }

    @NativeMethods
    interface Natives {
        void applyStringTranslateOption(
                long nativeTranslateCompactInfoBar,
                TranslateCompactInfoBar caller,
                int option,
                String value);

        void applyBoolTranslateOption(
                long nativeTranslateCompactInfoBar,
                TranslateCompactInfoBar caller,
                int option,
                boolean value);

        boolean shouldAutoNeverTranslate(
                long nativeTranslateCompactInfoBar,
                TranslateCompactInfoBar caller,
                boolean menuExpanded);

        boolean isIncognito(long nativeTranslateCompactInfoBar, TranslateCompactInfoBar caller);

        String[] getContentLanguagesCodes(
                long nativeTranslateCompactInfoBar, TranslateCompactInfoBar caller);
    }
}