// Copyright 2013 The Chromium Authors. All rights reserved.
// 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.content.Context;
import android.support.v7.widget.SwitchCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.view.View;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R;
import org.chromium.ui.base.DeviceFormFactor;
import java.util.ArrayList;
/**
* Java version of the translate infobar
*/
public class TranslateInfoBar extends InfoBar implements SubPanelListener {
// Needs to be kept in sync with the Type enum in translate_infobar_delegate.h.
public static final int BEFORE_TRANSLATE_INFOBAR = 0;
public static final int TRANSLATING_INFOBAR = 1;
public static final int AFTER_TRANSLATE_INFOBAR = 2;
public static final int TRANSLATE_ERROR_INFOBAR = 3;
public static final int MAX_INFOBAR_INDEX = 4;
// Defines what subpanel needs to be shown, if any
public static final int NO_PANEL = 0;
public static final int LANGUAGE_PANEL = 1;
public static final int NEVER_PANEL = 2;
public static final int ALWAYS_PANEL = 3;
public static final int MAX_PANEL_INDEX = 4;
private long mNativeTranslateInfoBarPtr;
private int mInfoBarType;
private final TranslateOptions mOptions;
private int mOptionsPanelViewType;
private TranslateSubPanel mSubPanel;
private final boolean mShouldShowNeverBar;
@CalledByNative
private static InfoBar show(int translateBarType, String sourceLanguageCode,
String targetLanguageCode, boolean autoTranslatePair, boolean showNeverInfobar,
boolean triggeredFromMenu, String[] languages, String[] codes) {
return new TranslateInfoBar(translateBarType, sourceLanguageCode, targetLanguageCode,
autoTranslatePair, showNeverInfobar, triggeredFromMenu, languages, codes);
}
private TranslateInfoBar(int infoBarType, String sourceLanguageCode, String targetLanguageCode,
boolean autoTranslatePair, boolean shouldShowNeverBar, boolean triggeredFromMenu,
String[] languages, String[] codes) {
super(R.drawable.infobar_translate, null, null);
assert languages.length == codes.length;
ArrayList<TranslateOptions.TranslateLanguagePair> languageList =
new ArrayList<TranslateOptions.TranslateLanguagePair>();
for (int i = 0; i < languages.length; ++i) {
languageList.add(new TranslateOptions.TranslateLanguagePair(codes[i], languages[i]));
}
mOptions = new TranslateOptions(sourceLanguageCode, targetLanguageCode, languageList,
autoTranslatePair, triggeredFromMenu);
mInfoBarType = infoBarType;
mShouldShowNeverBar = shouldShowNeverBar;
mOptionsPanelViewType = NO_PANEL;
}
@Override
public void onCloseButtonClicked() {
if (getInfoBarType() == BEFORE_TRANSLATE_INFOBAR && mOptionsPanelViewType == NO_PANEL) {
// Make it behave exactly as the Nope Button.
onButtonClicked(false);
} else {
super.onCloseButtonClicked();
}
}
@Override
public void onButtonClicked(boolean isPrimaryButton) {
if (mSubPanel != null) {
mSubPanel.onButtonClicked(isPrimaryButton);
return;
}
int action = actionFor(isPrimaryButton);
if (getInfoBarType() == BEFORE_TRANSLATE_INFOBAR && mOptionsPanelViewType == NO_PANEL
&& action == ActionType.CANCEL && needsNeverPanel()) {
// "Nope" was clicked and instead of dismissing we need to show
// the extra never panel.
swapPanel(NEVER_PANEL);
} else {
onTranslateInfoBarButtonClicked(action);
}
}
/**
* Based on the infobar and the button pressed figure out what action needs to happen.
*/
private int actionFor(boolean isPrimaryButton) {
int action = ActionType.NONE;
int infobarType = getInfoBarType();
switch (infobarType) {
case TranslateInfoBar.BEFORE_TRANSLATE_INFOBAR:
action = isPrimaryButton
? ActionType.TRANSLATE : ActionType.CANCEL;
break;
case TranslateInfoBar.AFTER_TRANSLATE_INFOBAR:
if (!isPrimaryButton) {
action = ActionType.TRANSLATE_SHOW_ORIGINAL;
}
break;
case TranslateInfoBar.TRANSLATE_ERROR_INFOBAR:
// retry
action = ActionType.TRANSLATE;
break;
default:
break;
}
return action;
}
private CharSequence getMessageText(Context context) {
switch (getInfoBarType()) {
case BEFORE_TRANSLATE_INFOBAR:
String template = context.getString(R.string.translate_infobar_text);
return formatBeforeInfoBarMessage(template, mOptions.sourceLanguageName(),
mOptions.targetLanguageName(), LANGUAGE_PANEL);
case AFTER_TRANSLATE_INFOBAR:
String translated = context.getString(
R.string.translate_infobar_translation_done, mOptions.targetLanguageName());
if (needsAlwaysPanel()) {
String moreOptions = context.getString(R.string.more);
return formatAfterTranslateInfoBarMessage(translated, moreOptions,
ALWAYS_PANEL);
} else {
return translated;
}
case TRANSLATING_INFOBAR:
return context.getString(
R.string.translate_infobar_translating, mOptions.targetLanguageName());
default:
return context.getString(R.string.translate_infobar_error);
}
}
private String getPrimaryButtonText(Context context) {
switch (getInfoBarType()) {
case BEFORE_TRANSLATE_INFOBAR:
return context.getString(R.string.translate_button);
case AFTER_TRANSLATE_INFOBAR:
if (!needsAlwaysPanel()) {
return context.getString(R.string.translate_button_done);
}
return null;
case TRANSLATE_ERROR_INFOBAR:
return context.getString(R.string.translate_retry);
default:
return null; // no inner buttons on the remaining infobars
}
}
private String getSecondaryButtonText(Context context) {
switch (getInfoBarType()) {
case BEFORE_TRANSLATE_INFOBAR:
return context.getString(R.string.translate_nope);
case AFTER_TRANSLATE_INFOBAR:
if (!needsAlwaysPanel()) {
return context.getString(R.string.translate_show_original);
}
return null;
default:
return null;
}
}
@Override
public void createContent(InfoBarLayout layout) {
if (mOptionsPanelViewType == NO_PANEL) {
mSubPanel = null;
} else {
mSubPanel = panelFor(mOptionsPanelViewType);
if (mSubPanel != null) {
mSubPanel.createContent(getContext(), layout);
}
return;
}
Context context = layout.getContext();
layout.setMessage(getMessageText(context));
layout.setButtons(getPrimaryButtonText(context), getSecondaryButtonText(context));
if (getInfoBarType() == AFTER_TRANSLATE_INFOBAR && !needsAlwaysPanel()
&& !mOptions.triggeredFromMenu()) {
// Fully expanded version of the "Always Translate" InfoBar.
TranslateAlwaysPanel.createAlwaysToggle(layout, mOptions);
}
}
// SubPanelListener implementation
@Override
public void onPanelClosed(int action) {
setControlsEnabled(false);
if (mOptionsPanelViewType == LANGUAGE_PANEL) {
// Close the sub panel and show the infobar again.
mOptionsPanelViewType = NO_PANEL;
replaceView(createView());
} else {
// Apply options and close the infobar.
onTranslateInfoBarButtonClicked(action);
}
}
private void onTranslateInfoBarButtonClicked(int action) {
onOptionsChanged();
onButtonClicked(action);
}
@Override
public void onOptionsChanged() {
if (mNativeTranslateInfoBarPtr == 0) return;
// Handle the "Always Translate" checkbox.
if (getInfoBarType() == AFTER_TRANSLATE_INFOBAR) {
SwitchCompat alwaysSwitch = (SwitchCompat) getView().findViewById(
R.id.translate_infobar_always_toggle);
mOptions.toggleAlwaysTranslateLanguageState(alwaysSwitch.isChecked());
}
if (mOptions.optionsChanged()) {
nativeApplyTranslateOptions(mNativeTranslateInfoBarPtr, mOptions.sourceLanguageCode(),
mOptions.targetLanguageCode(), mOptions.alwaysTranslateLanguageState(),
mOptions.neverTranslateLanguageState(), mOptions.neverTranslateDomainState());
}
}
private boolean needsNeverPanel() {
return (getInfoBarType() == TranslateInfoBar.BEFORE_TRANSLATE_INFOBAR
&& mShouldShowNeverBar);
}
private boolean needsAlwaysPanel() {
return (getInfoBarType() == TranslateInfoBar.AFTER_TRANSLATE_INFOBAR
&& mOptions.alwaysTranslateLanguageState()
&& !DeviceFormFactor.isTablet(getContext()));
}
/**
* @param newPanel id of the new panel to swap in. Use NO_PANEL to
* simply remove the current panel.
*/
private void swapPanel(int newPanel) {
assert (newPanel >= NO_PANEL && newPanel < MAX_PANEL_INDEX);
mOptionsPanelViewType = newPanel;
replaceView(createView());
}
/**
* @return a panel of the specified {@code type}
*/
private TranslateSubPanel panelFor(int type) {
assert (type >= NO_PANEL && type < MAX_PANEL_INDEX);
switch (type) {
case LANGUAGE_PANEL:
return new TranslateLanguagePanel(this, mOptions);
case NEVER_PANEL:
return new TranslateNeverPanel(this, mOptions);
case ALWAYS_PANEL:
return new TranslateAlwaysPanel(this, mOptions);
default:
return null;
}
}
/**
* @return a formatted message with links to {@code panelId}.
*/
private CharSequence formatBeforeInfoBarMessage(String template, String sourceLanguage,
String targetLanguage, final int panelId) {
SpannableString formattedSourceLanguage = new SpannableString(sourceLanguage);
formattedSourceLanguage.setSpan(new ClickableSpan() {
@Override
public void onClick(View view) {
swapPanel(panelId);
}
}, 0, sourceLanguage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableString formattedTargetLanguage = new SpannableString(targetLanguage);
formattedTargetLanguage.setSpan(new ClickableSpan() {
@Override
public void onClick(View view) {
swapPanel(panelId);
}
}, 0, targetLanguage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return TextUtils.expandTemplate(template, formattedSourceLanguage, formattedTargetLanguage);
}
/**
* @return a formatted message with a link to {@code panelId}
*/
private CharSequence formatAfterTranslateInfoBarMessage(String statement, String linkText,
final int panelId) {
SpannableStringBuilder result = new SpannableStringBuilder();
result.append(statement).append(" ");
SpannableString formattedChange = new SpannableString(linkText);
formattedChange.setSpan(new ClickableSpan() {
@Override
public void onClick(View view) {
swapPanel(panelId);
}
}, 0, linkText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
result.append(formattedChange);
return result;
}
int getInfoBarType() {
return mInfoBarType;
}
@CalledByNative
private void setNativePtr(long nativePtr) {
mNativeTranslateInfoBarPtr = nativePtr;
}
@Override
protected void onNativeDestroyed() {
mNativeTranslateInfoBarPtr = 0;
super.onNativeDestroyed();
}
@CalledByNative
private void changeTranslateInfoBarType(int infoBarType) {
if (infoBarType >= 0 && infoBarType < MAX_INFOBAR_INDEX) {
mInfoBarType = infoBarType;
replaceView(createView());
} else {
assert false : "Trying to change the InfoBar to a type that is invalid.";
}
}
private native void nativeApplyTranslateOptions(long nativeTranslateInfoBar,
String sourceLanguageCode, String targetLanguageCode, boolean alwaysTranslate,
boolean neverTranslateLanguage, boolean neverTranslateSite);
}