// 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.graphics.Color; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; import org.chromium.chrome.browser.infobar.InfoBar; import org.chromium.chrome.browser.infobar.InfoBarLayout; import org.chromium.chrome.R; import java.util.ArrayList; import java.util.List; /** * Language panel shown in the translate infobar. */ public class TranslateLanguagePanel implements TranslateSubPanel, AdapterView.OnItemSelectedListener { private static final int LANGUAGE_TYPE_SOURCE = 0; private static final int LANGUAGE_TYPE_TARGET = 1; // UI elements. private Spinner mSourceSpinner; private Spinner mTargetSpinner; // Items that are not interacted with. // Provided by the caller, the new languages will be set here if the user // clicks "done". private final TranslateOptions mOptions; // This object will be used to keep the state for the time the // panel is opened it can be totally discarded in the end if the user // clicks "cancel". private TranslateOptions mSessionOptions; private LanguageArrayAdapter mSourceAdapter; private LanguageArrayAdapter mTargetAdapter; private final SubPanelListener mListener; /** * Display language drop downs so they can be picked as source or * target for a translation. * * @param listener triggered when the panel is closed * @param options will be modified with the new languages selected. */ public TranslateLanguagePanel(SubPanelListener listener, TranslateOptions options) { mListener = listener; mOptions = options; mSessionOptions = new TranslateOptions(mOptions); } @Override public void createContent(Context context, InfoBarLayout layout) { mSourceSpinner = null; mTargetSpinner = null; String changeLanguage = context.getString(R.string.translate_infobar_change_languages); TextView panelMessage = (TextView) layout.findViewById(R.id.infobar_message); panelMessage.setText(changeLanguage); // Set up the spinners. createSpinners(context); layout.addGroup(mSourceSpinner, mTargetSpinner); // Set up the buttons. layout.addButtons(context.getString(R.string.translate_button_done), context.getString(R.string.cancel)); } @Override public void onButtonClicked(boolean primary) { if (primary) { mOptions.setSourceLanguage(mSessionOptions.sourceLanguageIndex()); mOptions.setTargetLanguage(mSessionOptions.targetLanguageIndex()); } mListener.onPanelClosed(InfoBar.ACTION_TYPE_NONE); } private void createSpinners(Context context) { mSourceAdapter = new LanguageArrayAdapter(context, R.layout.translate_spinner, LANGUAGE_TYPE_SOURCE); mTargetAdapter = new LanguageArrayAdapter(context, R.layout.translate_spinner, LANGUAGE_TYPE_TARGET); // Determine how wide each spinner needs to be to avoid truncating its children. mSourceAdapter.addAll(createSpinnerLanguages(-1)); mTargetAdapter.addAll(createSpinnerLanguages(-1)); mSourceAdapter.measureWidthRequiredForView(); mTargetAdapter.measureWidthRequiredForView(); // Create the spinners. mSourceSpinner = new Spinner(context); mTargetSpinner = new Spinner(context); mSourceSpinner.setOnItemSelectedListener(this); mTargetSpinner.setOnItemSelectedListener(this); mSourceSpinner.setAdapter(mSourceAdapter); mTargetSpinner.setAdapter(mTargetAdapter); reloadSpinners(); } private void reloadSpinners() { mSourceAdapter.clear(); mTargetAdapter.clear(); int sourceAvoidLanguage = mSessionOptions.targetLanguageIndex(); int targetAvoidLanguage = mSessionOptions.sourceLanguageIndex(); mSourceAdapter.addAll(createSpinnerLanguages(sourceAvoidLanguage)); mTargetAdapter.addAll(createSpinnerLanguages(targetAvoidLanguage)); int originalSourceSelection = mSourceSpinner.getSelectedItemPosition(); int newSourceSelection = getSelectionPosition(LANGUAGE_TYPE_SOURCE); if (originalSourceSelection != newSourceSelection) mSourceSpinner.setSelection(newSourceSelection); int originalTargetSelection = mTargetSpinner.getSelectedItemPosition(); int newTargetSelection = getSelectionPosition(LANGUAGE_TYPE_TARGET); if (originalTargetSelection != newTargetSelection) mTargetSpinner.setSelection(newTargetSelection); } private int getSelectionPosition(int languageType) { int position = languageType == LANGUAGE_TYPE_SOURCE ? mSessionOptions.sourceLanguageIndex() : mSessionOptions.targetLanguageIndex(); // Since the source and target languages cannot appear in both spinners, the index for the // source language can be off by one if comes after the target language alphabetically (and // vice versa). int opposite = languageType == LANGUAGE_TYPE_SOURCE ? mSessionOptions.targetLanguageIndex() : mSessionOptions.sourceLanguageIndex(); if (opposite < position) position -= 1; return position; } @Override public void onItemSelected(AdapterView<?> adapter, View view, int position, long id) { Spinner spinner = (Spinner) adapter; int newId = ((SpinnerLanguageElement) spinner.getSelectedItem()).getLanguageId(); if (spinner == mSourceSpinner) { mSessionOptions.setSourceLanguage(newId); } else { mSessionOptions.setTargetLanguage(newId); } reloadSpinners(); } @Override public void onNothingSelected(AdapterView<?> adapter) { } /** * Determines what languages will be shown in the Spinner. * @param avoidLanguage Index of the language to avoid. Use -1 to display all languages. */ private ArrayList<SpinnerLanguageElement> createSpinnerLanguages(int avoidLanguage) { ArrayList<SpinnerLanguageElement> result = new ArrayList<SpinnerLanguageElement>(); List<String> languages = mSessionOptions.allLanguages(); for (int i = 0; i < languages.size(); ++i) { if (i != avoidLanguage) { result.add(new SpinnerLanguageElement(languages.get(i), i)); } } return result; } /** * The drop down view displayed to show the currently selected value. */ private static class LanguageArrayAdapter extends ArrayAdapter<SpinnerLanguageElement> { private final SpannableString mTextTemplate; private int mMinimumWidth; public LanguageArrayAdapter(Context context, int textViewResourceId, int languageType) { super(context, textViewResourceId); // Get the string that we will display inside the Spinner, indicating whether the // spinner is used for the source or target language. String textTemplate = languageType == LANGUAGE_TYPE_SOURCE ? context.getString(R.string.translate_options_source_hint) : context.getString(R.string.translate_options_target_hint); mTextTemplate = new SpannableString(textTemplate); mTextTemplate.setSpan( new ForegroundColorSpan(Color.GRAY), 0, textTemplate.length(), 0); } /** Measures how large the view needs to be to avoid truncating its children. */ public void measureWidthRequiredForView() { mMinimumWidth = 0; final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); FrameLayout layout = new FrameLayout(getContext()); TextView estimator = (TextView) LayoutInflater.from(getContext()).inflate( R.layout.infobar_text, null); layout.addView(estimator); for (int i = 0; i < getCount(); ++i) { estimator.setText(getStringForLanguage(i)); estimator.measure(spec, spec); mMinimumWidth = Math.max(mMinimumWidth, estimator.getMeasuredWidth()); } } @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { TextView result; if (!(convertView instanceof TextView)) { result = (TextView) LayoutInflater.from(getContext()).inflate( R.layout.infobar_spinner_item, null); } else { result = (TextView) convertView; } String language = ((SpinnerLanguageElement) getItem(position)).toString(); result.setText(language); return result; } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView result; if (!(convertView instanceof TextView)) { result = (TextView) LayoutInflater.from(getContext()).inflate( R.layout.infobar_text, null); } else { result = (TextView) convertView; } result.setEllipsize(TextUtils.TruncateAt.END); result.setMaxLines(1); result.setText(getStringForLanguage(position)); result.setMinWidth(mMinimumWidth); return result; } private CharSequence getStringForLanguage(int position) { // The spinners prepend a string to show if they're for the source or target language. String language = getItem(position).toString(); SpannableString lang = new SpannableString(language); lang.setSpan(new ForegroundColorSpan(Color.BLACK), 0, lang.length(), 0); return TextUtils.expandTemplate(mTextTemplate, lang); } } /** * The element that goes inside the spinner. */ private static class SpinnerLanguageElement { private final String mLanguageName; private final int mLanguageId; public SpinnerLanguageElement(String languageName, int languageId) { mLanguageName = languageName; mLanguageId = languageId; } public int getLanguageId() { return mLanguageId; } /** * This is the text displayed in the spinner element so make sure no debug information * is added. */ @Override public String toString() { return mLanguageName; } } }