Home | History | Annotate | Download | only in infobar
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.chrome.browser.infobar;
      6 
      7 import android.content.Context;
      8 import android.text.Spannable;
      9 import android.text.SpannableString;
     10 import android.text.SpannableStringBuilder;
     11 import android.text.TextUtils;
     12 import android.text.style.ClickableSpan;
     13 import android.view.View;
     14 import android.widget.CheckBox;
     15 
     16 import org.chromium.chrome.R;
     17 import org.chromium.ui.base.DeviceFormFactor;
     18 
     19 /**
     20  * Java version of the translate infobar
     21  */
     22 public class TranslateInfoBar extends InfoBar implements SubPanelListener {
     23     // Needs to be kept in sync with the Type enum in translate_infobar_delegate.h.
     24     public static final int BEFORE_TRANSLATE_INFOBAR = 0;
     25     public static final int TRANSLATING_INFOBAR = 1;
     26     public static final int AFTER_TRANSLATE_INFOBAR = 2;
     27     public static final int TRANSLATE_ERROR_INFOBAR = 3;
     28     public static final int MAX_INFOBAR_INDEX = 4;
     29 
     30     // Defines what subpanel needs to be shown, if any
     31     public static final int NO_PANEL = 0;
     32     public static final int LANGUAGE_PANEL = 1;
     33     public static final int NEVER_PANEL = 2;
     34     public static final int ALWAYS_PANEL = 3;
     35     public static final int MAX_PANEL_INDEX = 4;
     36 
     37     private int mInfoBarType;
     38     private final TranslateOptions mOptions;
     39     private int mOptionsPanelViewType;
     40     private TranslateSubPanel mSubPanel;
     41     private final boolean mShouldShowNeverBar;
     42     private final TranslateInfoBarDelegate mTranslateDelegate;
     43 
     44     public TranslateInfoBar(long nativeInfoBarPtr, TranslateInfoBarDelegate delegate,
     45             int infoBarType, int sourceLanguageIndex, int targetLanguageIndex,
     46             boolean autoTranslatePair, boolean shouldShowNeverBar,
     47             boolean triggeredFromMenu, String[] languages) {
     48         super(null, R.drawable.infobar_translate, null);
     49         mTranslateDelegate = delegate;
     50         mOptions = new TranslateOptions(sourceLanguageIndex, targetLanguageIndex, languages,
     51                 autoTranslatePair, triggeredFromMenu);
     52         mInfoBarType = infoBarType;
     53         mShouldShowNeverBar = shouldShowNeverBar;
     54         mOptionsPanelViewType = NO_PANEL;
     55         setNativeInfoBar(nativeInfoBarPtr);
     56     }
     57 
     58     @Override
     59     public void onCloseButtonClicked() {
     60         if (getInfoBarType() == BEFORE_TRANSLATE_INFOBAR && mOptionsPanelViewType == NO_PANEL) {
     61             // Make it behave exactly as the Nope Button.
     62             onButtonClicked(false);
     63         } else {
     64             nativeOnCloseButtonClicked(mNativeInfoBarPtr);
     65         }
     66     }
     67 
     68     @Override
     69     public void onButtonClicked(boolean isPrimaryButton) {
     70         if (mSubPanel != null) {
     71             mSubPanel.onButtonClicked(isPrimaryButton);
     72             return;
     73         }
     74 
     75         int action = actionFor(isPrimaryButton);
     76 
     77         if (getInfoBarType() == BEFORE_TRANSLATE_INFOBAR && mOptionsPanelViewType == NO_PANEL
     78                 && action == ACTION_TYPE_CANCEL && needsNeverPanel()) {
     79             // "Nope" was clicked and instead of dismissing we need to show
     80             // the extra never panel.
     81             swapPanel(NEVER_PANEL);
     82         } else {
     83             onTranslateInfoBarButtonClicked(action);
     84         }
     85     }
     86 
     87     /**
     88      * Based on the infobar and the button pressed figure out what action needs to happen.
     89      */
     90     private int actionFor(boolean isPrimaryButton) {
     91         int action = InfoBar.ACTION_TYPE_NONE;
     92         int infobarType = getInfoBarType();
     93         switch (infobarType) {
     94             case TranslateInfoBar.BEFORE_TRANSLATE_INFOBAR:
     95                 action = isPrimaryButton
     96                         ? InfoBar.ACTION_TYPE_TRANSLATE : InfoBar.ACTION_TYPE_CANCEL;
     97                 break;
     98             case TranslateInfoBar.AFTER_TRANSLATE_INFOBAR:
     99                 if (!isPrimaryButton) {
    100                     action = InfoBar.ACTION_TYPE_TRANSLATE_SHOW_ORIGINAL;
    101                 }
    102                 break;
    103             case TranslateInfoBar.TRANSLATE_ERROR_INFOBAR:
    104                 // retry
    105                 action = InfoBar.ACTION_TYPE_TRANSLATE;
    106                 break;
    107             default:
    108                 break;
    109         }
    110         return action;
    111     }
    112 
    113     private CharSequence getMessageText(Context context) {
    114         switch (getInfoBarType()) {
    115             case BEFORE_TRANSLATE_INFOBAR:
    116                 String template = context.getString(R.string.translate_infobar_text);
    117                 return formatBeforeInfoBarMessage(template, mOptions.sourceLanguage(),
    118                         mOptions.targetLanguage(), LANGUAGE_PANEL);
    119             case AFTER_TRANSLATE_INFOBAR:
    120                 String translated = context.getString(
    121                         R.string.translate_infobar_translation_done, mOptions.targetLanguage());
    122                 if (needsAlwaysPanel()) {
    123                     String moreOptions = context.getString(
    124                             R.string.translate_infobar_translation_more_options);
    125                     return formatAfterTranslateInfoBarMessage(translated, moreOptions,
    126                             ALWAYS_PANEL);
    127                 } else {
    128                     return translated;
    129                 }
    130             case TRANSLATING_INFOBAR:
    131                 return context.getString(R.string.translate_infobar_translating,
    132                         mOptions.targetLanguage());
    133             default:
    134                 return context.getString(R.string.translate_infobar_error);
    135         }
    136     }
    137 
    138     private String getPrimaryButtonText(Context context) {
    139         switch (getInfoBarType()) {
    140             case BEFORE_TRANSLATE_INFOBAR:
    141                 return context.getString(R.string.translate_button);
    142             case AFTER_TRANSLATE_INFOBAR:
    143                 if (!needsAlwaysPanel()) {
    144                     return context.getString(R.string.translate_button_done);
    145                 }
    146                 return null;
    147             case TRANSLATE_ERROR_INFOBAR:
    148                 return context.getString(R.string.translate_retry);
    149             default:
    150                 return null; // no inner buttons on the remaining infobars
    151         }
    152     }
    153 
    154     private String getSecondaryButtonText(Context context) {
    155         switch (getInfoBarType()) {
    156             case BEFORE_TRANSLATE_INFOBAR:
    157                 return context.getString(R.string.translate_nope);
    158             case AFTER_TRANSLATE_INFOBAR:
    159                 if (!needsAlwaysPanel()) {
    160                     return context.getString(R.string.translate_show_original);
    161                 }
    162                 return null;
    163             default:
    164                 return null;
    165         }
    166     }
    167 
    168     @Override
    169     public void createContent(InfoBarLayout layout) {
    170         if (mOptionsPanelViewType == NO_PANEL) {
    171             mSubPanel = null;
    172         } else {
    173             mSubPanel = panelFor(mOptionsPanelViewType);
    174             if (mSubPanel != null) {
    175                 mSubPanel.createContent(getContext(), layout);
    176             }
    177             return;
    178         }
    179 
    180         Context context = layout.getContext();
    181         layout.setMessage(getMessageText(context));
    182         layout.setButtons(getPrimaryButtonText(context), getSecondaryButtonText(context));
    183 
    184         if (getInfoBarType() == AFTER_TRANSLATE_INFOBAR &&
    185                 !needsAlwaysPanel() &&
    186                 !mOptions.triggeredFromMenu()) {
    187             // Long always translate version
    188             TranslateCheckBox checkBox = new TranslateCheckBox(context, mOptions, this);
    189             layout.setCustomContent(checkBox);
    190         }
    191     }
    192 
    193     // SubPanelListener implementation
    194     @Override
    195     public void onPanelClosed(int action) {
    196         setControlsEnabled(false);
    197         if (mOptionsPanelViewType == LANGUAGE_PANEL) {
    198             // Close the sub panel and show the infobar again.
    199             mOptionsPanelViewType = NO_PANEL;
    200             updateViewForCurrentState(createView());
    201         } else {
    202             // Apply options and close the infobar.
    203             onTranslateInfoBarButtonClicked(action);
    204         }
    205     }
    206 
    207     private void onTranslateInfoBarButtonClicked(int action) {
    208         onOptionsChanged();
    209 
    210         // We need to re-check if the pointer is null now because applying options (like never
    211         // translate this site) can sometimes trigger closing the InfoBar.
    212         if (mNativeInfoBarPtr == 0) return;
    213         nativeOnButtonClicked(mNativeInfoBarPtr, action, "");
    214     }
    215 
    216     @Override
    217     public void setControlsEnabled(boolean state) {
    218         super.setControlsEnabled(state);
    219 
    220         // Handle the "Always Translate" checkbox.
    221         ContentWrapperView wrapper = getContentWrapper(false);
    222         if (wrapper != null) {
    223             CheckBox checkBox = (CheckBox) wrapper.findViewById(R.id.infobar_extra_check);
    224             if (checkBox != null) checkBox.setEnabled(state);
    225         }
    226     }
    227 
    228     @Override
    229     public void onOptionsChanged() {
    230         if (mNativeInfoBarPtr == 0) return;
    231 
    232         if (mOptions.optionsChanged()) {
    233             mTranslateDelegate.applyTranslateOptions(mNativeInfoBarPtr,
    234                     mOptions.sourceLanguageIndex(),
    235                     mOptions.targetLanguageIndex(),
    236                     mOptions.alwaysTranslateLanguageState(),
    237                     mOptions.neverTranslateLanguageState(),
    238                     mOptions.neverTranslateDomainState());
    239         }
    240     }
    241 
    242     private boolean needsNeverPanel() {
    243         return (getInfoBarType() == TranslateInfoBar.BEFORE_TRANSLATE_INFOBAR
    244                 && mShouldShowNeverBar);
    245     }
    246 
    247     private boolean needsAlwaysPanel() {
    248         return (getInfoBarType() == TranslateInfoBar.AFTER_TRANSLATE_INFOBAR
    249                 && mOptions.alwaysTranslateLanguageState()
    250                 && !DeviceFormFactor.isTablet(getContext()));
    251     }
    252 
    253     /**
    254      * @param newPanel id of the new panel to swap in. Use NO_PANEL to
    255      *     simply remove the current panel.
    256      */
    257     private void swapPanel(int newPanel) {
    258         assert (newPanel >= NO_PANEL && newPanel < MAX_PANEL_INDEX);
    259         mOptionsPanelViewType = newPanel;
    260         updateViewForCurrentState(createView());
    261     }
    262 
    263     /**
    264      * @return a panel of the specified {@code type}
    265      */
    266     private TranslateSubPanel panelFor(int type) {
    267         assert (type >= NO_PANEL && type < MAX_PANEL_INDEX);
    268         switch (type) {
    269             case LANGUAGE_PANEL:
    270                 return new TranslateLanguagePanel(this, mOptions);
    271             case NEVER_PANEL:
    272                 return new TranslateNeverPanel(this, mOptions);
    273             case ALWAYS_PANEL:
    274                 return new TranslateAlwaysPanel(this, mOptions);
    275             default:
    276                 return null;
    277         }
    278     }
    279 
    280     /**
    281      * Swaps out the current view in the ContentViewWrapper.
    282      */
    283     private void updateViewForCurrentState(View replacement) {
    284         setControlsEnabled(false);
    285         getInfoBarContainer().swapInfoBarViews(this, replacement);
    286     }
    287 
    288     /**
    289      * @return a formatted message with links to {@code panelId}.
    290      */
    291     private CharSequence formatBeforeInfoBarMessage(String template, String sourceLanguage,
    292             String targetLanguage, final int panelId) {
    293 
    294         SpannableString formattedSourceLanguage = new SpannableString(sourceLanguage);
    295         formattedSourceLanguage.setSpan(new ClickableSpan() {
    296             @Override
    297             public void onClick(View view) {
    298                 swapPanel(panelId);
    299             }
    300         }, 0, sourceLanguage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    301 
    302         SpannableString formattedTargetLanguage = new SpannableString(targetLanguage);
    303         formattedTargetLanguage.setSpan(new ClickableSpan() {
    304             @Override
    305             public void onClick(View view) {
    306                 swapPanel(panelId);
    307             }
    308         }, 0, targetLanguage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    309 
    310         return TextUtils.expandTemplate(template, formattedSourceLanguage, formattedTargetLanguage);
    311     }
    312 
    313     /**
    314      * @return a formatted message with a link to {@code panelId}
    315      */
    316     private CharSequence formatAfterTranslateInfoBarMessage(String statement, String linkText,
    317             final int panelId) {
    318         SpannableStringBuilder result = new SpannableStringBuilder();
    319         result.append(statement).append(" ");
    320         SpannableString formattedChange = new SpannableString(linkText);
    321         formattedChange.setSpan(new ClickableSpan() {
    322             @Override
    323             public void onClick(View view) {
    324                 swapPanel(panelId);
    325             }
    326         }, 0, linkText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    327         result.append(formattedChange);
    328         return result;
    329     }
    330 
    331     int getInfoBarType() {
    332         return mInfoBarType;
    333     }
    334 
    335     void changeInfoBarTypeAndNativePointer(int infoBarType, long newNativePointer) {
    336         if (infoBarType >= 0 && infoBarType < MAX_INFOBAR_INDEX) {
    337             mInfoBarType = infoBarType;
    338             replaceNativePointer(newNativePointer);
    339             updateViewForCurrentState(createView());
    340         } else {
    341             assert false : "Trying to change the InfoBar to a type that is invalid.";
    342         }
    343     }
    344 }
    345